Page MenuHomePhabricator

D14251.id34411.diff
No OneTemporary

D14251.id34411.diff

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

Mime Type
text/plain
Expires
Sat, May 11, 1:01 AM (3 w, 1 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/yw/hn/36cypdipdg5uootc
Default Alt Text
D14251.id34411.diff (40 KB)

Event Timeline