Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F19515675
D14251.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
40 KB
Referenced Files
None
Subscribers
None
D14251.diff
View Options
diff --git a/resources/sql/autopatches/20151009.drydock.auth.1.sql b/resources/sql/autopatches/20151009.drydock.auth.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20151009.drydock.auth.1.sql
@@ -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};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -799,6 +799,14 @@
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.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',
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
@@ -2946,6 +2954,7 @@
'PhabricatorSpacesTestCase' => 'applications/spaces/__tests__/PhabricatorSpacesTestCase.php',
'PhabricatorSpacesViewController' => 'applications/spaces/controller/PhabricatorSpacesViewController.php',
'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
+ 'PhabricatorStandardCustomFieldBlueprints' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php',
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
'PhabricatorStandardCustomFieldDatasource' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDatasource.php',
@@ -4537,6 +4546,17 @@
'DoorkeeperTagsController' => 'PhabricatorController',
'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
+ 'DrydockAuthorization' => array(
+ 'DrydockDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'DrydockAuthorizationAuthorizeController' => 'DrydockController',
+ 'DrydockAuthorizationListController' => 'DrydockController',
+ 'DrydockAuthorizationListView' => 'AphrontView',
+ 'DrydockAuthorizationPHIDType' => 'PhabricatorPHIDType',
+ 'DrydockAuthorizationQuery' => 'DrydockQuery',
+ 'DrydockAuthorizationSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'DrydockAuthorizationViewController' => 'DrydockController',
'DrydockBlueprint' => array(
'DrydockDAO',
'PhabricatorApplicationTransactionInterface',
@@ -7091,6 +7111,7 @@
'PhabricatorSpacesTestCase' => 'PhabricatorTestCase',
'PhabricatorSpacesViewController' => 'PhabricatorSpacesController',
'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
+ 'PhabricatorStandardCustomFieldBlueprints' => 'PhabricatorStandardCustomFieldTokenizer',
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldDatasource' => 'PhabricatorStandardCustomFieldTokenizer',
diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php
--- a/src/applications/drydock/application/PhabricatorDrydockApplication.php
+++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php
@@ -57,6 +57,8 @@
'DrydockResourceListController',
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLogListController',
+ 'authorizations/(?:query/(?P<queryKey>[^/]+)/)?' =>
+ 'DrydockAuthorizationListController',
),
'create/' => 'DrydockBlueprintCreateController',
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
@@ -81,6 +83,13 @@
'DrydockLogListController',
),
),
+ '(?P<type>authorization)/' => array(
+ '(?P<id>[1-9]\d*)/' => array(
+ '' => 'DrydockAuthorizationViewController',
+ '(?P<action>authorize|decline)/' =>
+ 'DrydockAuthorizationAuthorizeController',
+ ),
+ ),
),
);
}
diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
@@ -390,5 +390,15 @@
return $lease;
}
+ public function getFieldSpecifications() {
+ return array(
+ 'blueprintPHIDs' => array(
+ 'name' => pht('Use Blueprints'),
+ 'type' => 'blueprints',
+ 'required' => true,
+ ),
+ ) + parent::getFieldSpecifications();
+ }
+
}
diff --git a/src/applications/drydock/controller/DrydockAuthorizationAuthorizeController.php b/src/applications/drydock/controller/DrydockAuthorizationAuthorizeController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockAuthorizationAuthorizeController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockAuthorizationListController.php b/src/applications/drydock/controller/DrydockAuthorizationListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockAuthorizationListController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockAuthorizationViewController.php b/src/applications/drydock/controller/DrydockAuthorizationViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockAuthorizationViewController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php
--- a/src/applications/drydock/controller/DrydockBlueprintViewController.php
+++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php
@@ -51,6 +51,8 @@
$resource_box = $this->buildResourceBox($blueprint);
+ $authorizations_box = $this->buildAuthorizationsBox($blueprint);
+
$timeline = $this->buildTransactionTimeline(
$blueprint,
new DrydockBlueprintTransactionQuery());
@@ -68,6 +70,7 @@
$crumbs,
$object_box,
$resource_box,
+ $authorizations_box,
$log_box,
$timeline,
),
@@ -167,12 +170,78 @@
->setTag('a')
->setHref($resources_uri)
->setIconFont('fa-search')
- ->setText(pht('View All Resources')));
+ ->setText(pht('View All')));
return id(new PHUIObjectBoxView())
->setHeader($resource_header)
->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);
+
+ }
+
}
diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php
--- a/src/applications/drydock/controller/DrydockController.php
+++ b/src/applications/drydock/controller/DrydockController.php
@@ -105,7 +105,7 @@
->setTag('a')
->setHref($all_uri)
->setIconFont('fa-search')
- ->setText(pht('View All Logs')));
+ ->setText(pht('View All')));
return id(new PHUIObjectBoxView())
->setHeader($log_header)
diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php
--- a/src/applications/drydock/controller/DrydockResourceViewController.php
+++ b/src/applications/drydock/controller/DrydockResourceViewController.php
@@ -170,7 +170,7 @@
->setTag('a')
->setHref($leases_uri)
->setIconFont('fa-search')
- ->setText(pht('View All Leases')));
+ ->setText(pht('View All')));
$lease_list = id(new DrydockLeaseListView())
->setUser($viewer)
diff --git a/src/applications/drydock/customfield/DrydockBlueprintCoreCustomField.php b/src/applications/drydock/customfield/DrydockBlueprintCoreCustomField.php
--- a/src/applications/drydock/customfield/DrydockBlueprintCoreCustomField.php
+++ b/src/applications/drydock/customfield/DrydockBlueprintCoreCustomField.php
@@ -41,11 +41,6 @@
$object->setDetail($key, $value);
}
- public function applyApplicationTransactionExternalEffects(
- PhabricatorApplicationTransaction $xaction) {
- return;
- }
-
public function getBlueprintFieldValue() {
return $this->getProxy()->getFieldValue();
}
diff --git a/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php
@@ -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}/");
+ }
+ }
+
+}
diff --git a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php
--- a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php
+++ b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php
@@ -28,9 +28,12 @@
foreach ($handles as $phid => $handle) {
$blueprint = $objects[$phid];
$id = $blueprint->getID();
+ $name = $blueprint->getBlueprintName();
- $handle->setName($blueprint->getBlueprintName());
- $handle->setURI("/drydock/blueprint/{$id}/");
+ $handle
+ ->setName($name)
+ ->setFullName(pht('Blueprint %d: %s', $id, $name))
+ ->setURI("/drydock/blueprint/{$id}/");
}
}
diff --git a/src/applications/drydock/query/DrydockAuthorizationQuery.php b/src/applications/drydock/query/DrydockAuthorizationQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/query/DrydockAuthorizationQuery.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/drydock/storage/DrydockAuthorization.php b/src/applications/drydock/storage/DrydockAuthorization.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/storage/DrydockAuthorization.php
@@ -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.');
+ }
+
+
+}
diff --git a/src/applications/drydock/view/DrydockAuthorizationListView.php b/src/applications/drydock/view/DrydockAuthorizationListView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/view/DrydockAuthorizationListView.php
@@ -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;
+ }
+
+}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
@@ -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;
+ }
+
+
+
+}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
@@ -217,7 +217,7 @@
return array();
}
- private function decodeValue($value) {
+ protected function decodeValue($value) {
$value = json_decode($value);
if (!is_array($value)) {
$value = array();
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 15, 2:41 PM (13 h, 4 m)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/ah/ei/g3vbxrhipup2wufv
Default Alt Text
D14251.diff (40 KB)
Attached To
Mode
D14251: Rough cut of "Blueprint Authorizations"
Attached
Detach File
Event Timeline
Log In to Comment