@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add AlmanacBinding, to bind a service to an interface

Summary: Ref T5833. Allows you to bind a service (like `db.example.com`) to one or more interfaces (for example, to specify a pool with one read/write host and two read-only hosts). You can't configure which hosts have which properties yet, but you can add all the relevant interfaces to the service. Next diff will start supporting service, binding, and device properties like "is writable", "is active", etc., so that Almanac will be able to express operations like "change which database is writable", "disable writes", "bring a device down", etc.

Test Plan: See screenshots.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5833

Differential Revision: https://secure.phabricator.com/D10745

+1039 -3
+14
resources/sql/autopatches/20141017.almanac.binding.sql
··· 1 + CREATE TABLE {$NAMESPACE}_almanac.almanac_binding ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + servicePHID VARBINARY(64) NOT NULL, 5 + devicePHID VARBINARY(64) NOT NULL, 6 + interfacePHID VARBINARY(64) NOT NULL, 7 + mailKey BINARY(20) NOT NULL, 8 + dateCreated INT UNSIGNED NOT NULL, 9 + dateModified INT UNSIGNED NOT NULL, 10 + UNIQUE KEY `key_phid` (phid), 11 + UNIQUE KEY `key_service` (servicePHID, interfacePHID), 12 + KEY `key_device` (devicePHID), 13 + KEY `key_interface` (interfacePHID) 14 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+19
resources/sql/autopatches/20141017.almanac.bxaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_almanac.almanac_bindingtransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARCHAR(64) COLLATE utf8_bin NOT NULL, 4 + authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL, 5 + objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL, 6 + viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, 7 + editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, 8 + commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL, 11 + oldValue LONGTEXT COLLATE utf8_bin NOT NULL, 12 + newValue LONGTEXT COLLATE utf8_bin NOT NULL, 13 + contentSource LONGTEXT COLLATE utf8_bin NOT NULL, 14 + metadata LONGTEXT COLLATE utf8_bin NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+23
src/__phutil_library_map__.php
··· 10 10 '__library_version__' => 2, 11 11 'class' => array( 12 12 'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php', 13 + 'AlmanacBinding' => 'applications/almanac/storage/AlmanacBinding.php', 14 + 'AlmanacBindingEditController' => 'applications/almanac/controller/AlmanacBindingEditController.php', 15 + 'AlmanacBindingEditor' => 'applications/almanac/editor/AlmanacBindingEditor.php', 16 + 'AlmanacBindingPHIDType' => 'applications/almanac/phid/AlmanacBindingPHIDType.php', 17 + 'AlmanacBindingQuery' => 'applications/almanac/query/AlmanacBindingQuery.php', 18 + 'AlmanacBindingTableView' => 'applications/almanac/view/AlmanacBindingTableView.php', 19 + 'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php', 20 + 'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php', 21 + 'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php', 13 22 'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php', 14 23 'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php', 15 24 'AlmanacController' => 'applications/almanac/controller/AlmanacController.php', ··· 30 39 'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php', 31 40 'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.php', 32 41 'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php', 42 + 'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php', 33 43 'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php', 34 44 'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php', 35 45 'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php', ··· 2947 2957 ), 2948 2958 'xmap' => array( 2949 2959 'AlmanacAddress' => 'Phobject', 2960 + 'AlmanacBinding' => array( 2961 + 'AlmanacDAO', 2962 + 'PhabricatorPolicyInterface', 2963 + ), 2964 + 'AlmanacBindingEditController' => 'AlmanacServiceController', 2965 + 'AlmanacBindingEditor' => 'PhabricatorApplicationTransactionEditor', 2966 + 'AlmanacBindingPHIDType' => 'PhabricatorPHIDType', 2967 + 'AlmanacBindingQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2968 + 'AlmanacBindingTableView' => 'AphrontView', 2969 + 'AlmanacBindingTransaction' => 'PhabricatorApplicationTransaction', 2970 + 'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 2971 + 'AlmanacBindingViewController' => 'AlmanacServiceController', 2950 2972 'AlmanacConduitUtil' => 'Phobject', 2951 2973 'AlmanacConsoleController' => 'AlmanacController', 2952 2974 'AlmanacController' => 'PhabricatorController', ··· 2973 2995 'AlmanacDAO', 2974 2996 'PhabricatorPolicyInterface', 2975 2997 ), 2998 + 'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource', 2976 2999 'AlmanacInterfaceEditController' => 'AlmanacDeviceController', 2977 3000 'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType', 2978 3001 'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+4
src/applications/almanac/application/PhabricatorAlmanacApplication.php
··· 47 47 'interface/' => array( 48 48 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController', 49 49 ), 50 + 'binding/' => array( 51 + 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacBindingEditController', 52 + '(?P<id>\d+)/' => 'AlmanacBindingViewController', 53 + ), 50 54 'network/' => array( 51 55 '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController', 52 56 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
+128
src/applications/almanac/controller/AlmanacBindingEditController.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingEditController 4 + extends AlmanacServiceController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + 9 + $id = $request->getURIData('id'); 10 + if ($id) { 11 + $binding = id(new AlmanacBindingQuery()) 12 + ->setViewer($viewer) 13 + ->withIDs(array($id)) 14 + ->requireCapabilities( 15 + array( 16 + PhabricatorPolicyCapability::CAN_VIEW, 17 + PhabricatorPolicyCapability::CAN_EDIT, 18 + )) 19 + ->executeOne(); 20 + if (!$binding) { 21 + return new Aphront404Response(); 22 + } 23 + 24 + $service = $binding->getService(); 25 + $is_new = false; 26 + 27 + $service_uri = $service->getURI(); 28 + $cancel_uri = $binding->getURI(); 29 + $title = pht('Edit Binding'); 30 + $save_button = pht('Save Changes'); 31 + } else { 32 + $service = id(new AlmanacServiceQuery()) 33 + ->setViewer($viewer) 34 + ->withIDs(array($request->getStr('serviceID'))) 35 + ->requireCapabilities( 36 + array( 37 + PhabricatorPolicyCapability::CAN_VIEW, 38 + PhabricatorPolicyCapability::CAN_EDIT, 39 + )) 40 + ->executeOne(); 41 + 42 + $binding = AlmanacBinding::initializeNewBinding($service); 43 + $is_new = true; 44 + 45 + $service_uri = $service->getURI(); 46 + $cancel_uri = $service_uri; 47 + $title = pht('Create Binding'); 48 + $save_button = pht('Create Binding'); 49 + } 50 + 51 + $v_interface = array(); 52 + if ($binding->getInterfacePHID()) { 53 + $v_interface = array($binding->getInterfacePHID()); 54 + } 55 + $e_interface = true; 56 + 57 + $validation_exception = null; 58 + if ($request->isFormPost()) { 59 + $v_interface = $request->getArr('interfacePHIDs'); 60 + 61 + $type_interface = AlmanacBindingTransaction::TYPE_INTERFACE; 62 + 63 + $xactions = array(); 64 + 65 + $xactions[] = id(new AlmanacBindingTransaction()) 66 + ->setTransactionType($type_interface) 67 + ->setNewValue(head($v_interface)); 68 + 69 + $editor = id(new AlmanacBindingEditor()) 70 + ->setActor($viewer) 71 + ->setContentSourceFromRequest($request) 72 + ->setContinueOnNoEffect(true); 73 + 74 + try { 75 + $editor->applyTransactions($binding, $xactions); 76 + 77 + $binding_uri = $binding->getURI(); 78 + return id(new AphrontRedirectResponse())->setURI($binding_uri); 79 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 80 + $validation_exception = $ex; 81 + $e_interface = $ex->getShortMessage($type_interface); 82 + } 83 + } 84 + 85 + $interface_handles = array(); 86 + if ($v_interface) { 87 + $interface_handles = $this->loadViewerHandles($v_interface); 88 + } 89 + 90 + $form = id(new AphrontFormView()) 91 + ->setUser($viewer) 92 + ->appendChild( 93 + id(new AphrontFormTokenizerControl()) 94 + ->setName('interfacePHIDs') 95 + ->setLabel('Interface') 96 + ->setLimit(1) 97 + ->setDatasource(new AlmanacInterfaceDatasource()) 98 + ->setValue($interface_handles) 99 + ->setError($e_interface)) 100 + ->appendChild( 101 + id(new AphrontFormSubmitControl()) 102 + ->addCancelButton($cancel_uri) 103 + ->setValue($save_button)); 104 + 105 + $box = id(new PHUIObjectBoxView()) 106 + ->setValidationException($validation_exception) 107 + ->setHeaderText($title) 108 + ->appendChild($form); 109 + 110 + $crumbs = $this->buildApplicationCrumbs(); 111 + $crumbs->addTextCrumb($service->getName(), $service_uri); 112 + if ($is_new) { 113 + $crumbs->addTextCrumb(pht('Create Binding')); 114 + } else { 115 + $crumbs->addTextCrumb(pht('Edit Binding')); 116 + } 117 + 118 + return $this->buildApplicationPage( 119 + array( 120 + $crumbs, 121 + $box, 122 + ), 123 + array( 124 + 'title' => $title, 125 + )); 126 + } 127 + 128 + }
+122
src/applications/almanac/controller/AlmanacBindingViewController.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingViewController 4 + extends AlmanacServiceController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $request->getViewer(); 12 + 13 + $id = $request->getURIData('id'); 14 + 15 + $binding = id(new AlmanacBindingQuery()) 16 + ->setViewer($viewer) 17 + ->withIDs(array($id)) 18 + ->executeOne(); 19 + if (!$binding) { 20 + return new Aphront404Response(); 21 + } 22 + 23 + $service = $binding->getService(); 24 + $service_uri = $service->getURI(); 25 + 26 + $title = pht('Binding %s', $binding->getID()); 27 + 28 + $property_list = $this->buildPropertyList($binding); 29 + $action_list = $this->buildActionList($binding); 30 + $property_list->setActionList($action_list); 31 + 32 + $header = id(new PHUIHeaderView()) 33 + ->setUser($viewer) 34 + ->setHeader($title) 35 + ->setPolicyObject($binding); 36 + 37 + $box = id(new PHUIObjectBoxView()) 38 + ->setHeader($header) 39 + ->addPropertyList($property_list); 40 + 41 + $crumbs = $this->buildApplicationCrumbs(); 42 + $crumbs->addTextCrumb($service->getName(), $service_uri); 43 + $crumbs->addTextCrumb($title); 44 + 45 + $xactions = id(new AlmanacBindingTransactionQuery()) 46 + ->setViewer($viewer) 47 + ->withObjectPHIDs(array($binding->getPHID())) 48 + ->execute(); 49 + 50 + $xaction_view = id(new PhabricatorApplicationTransactionView()) 51 + ->setUser($viewer) 52 + ->setObjectPHID($binding->getPHID()) 53 + ->setTransactions($xactions) 54 + ->setShouldTerminate(true); 55 + 56 + return $this->buildApplicationPage( 57 + array( 58 + $crumbs, 59 + $box, 60 + $xaction_view, 61 + ), 62 + array( 63 + 'title' => $title, 64 + )); 65 + } 66 + 67 + private function buildPropertyList(AlmanacBinding $binding) { 68 + $viewer = $this->getViewer(); 69 + 70 + $properties = id(new PHUIPropertyListView()) 71 + ->setUser($viewer); 72 + 73 + $handles = $this->loadViewerHandles( 74 + array( 75 + $binding->getServicePHID(), 76 + $binding->getDevicePHID(), 77 + $binding->getInterface()->getNetworkPHID(), 78 + )); 79 + 80 + $properties->addProperty( 81 + pht('Service'), 82 + $handles[$binding->getServicePHID()]->renderLink()); 83 + 84 + $properties->addProperty( 85 + pht('Device'), 86 + $handles[$binding->getDevicePHID()]->renderLink()); 87 + 88 + $properties->addProperty( 89 + pht('Network'), 90 + $handles[$binding->getInterface()->getNetworkPHID()]->renderLink()); 91 + 92 + $properties->addProperty( 93 + pht('Interface'), 94 + $binding->getInterface()->renderDisplayAddress()); 95 + 96 + return $properties; 97 + } 98 + 99 + private function buildActionList(AlmanacBinding $binding) { 100 + $viewer = $this->getViewer(); 101 + $id = $binding->getID(); 102 + 103 + $can_edit = PhabricatorPolicyFilter::hasCapability( 104 + $viewer, 105 + $binding, 106 + PhabricatorPolicyCapability::CAN_EDIT); 107 + 108 + $actions = id(new PhabricatorActionListView()) 109 + ->setUser($viewer); 110 + 111 + $actions->addAction( 112 + id(new PhabricatorActionView()) 113 + ->setIcon('fa-pencil') 114 + ->setName(pht('Edit Binding')) 115 + ->setHref($this->getApplicationURI("binding/edit/{$id}/")) 116 + ->setWorkflow(!$can_edit) 117 + ->setDisabled(!$can_edit)); 118 + 119 + return $actions; 120 + } 121 + 122 + }
+50
src/applications/almanac/controller/AlmanacServiceViewController.php
··· 35 35 ->setHeader($header) 36 36 ->addPropertyList($property_list); 37 37 38 + $bindings = $this->buildBindingList($service); 39 + 38 40 $crumbs = $this->buildApplicationCrumbs(); 39 41 $crumbs->addTextCrumb($service->getName()); 40 42 ··· 53 55 array( 54 56 $crumbs, 55 57 $box, 58 + $bindings, 56 59 $xaction_view, 57 60 ), 58 61 array( ··· 90 93 ->setDisabled(!$can_edit)); 91 94 92 95 return $actions; 96 + } 97 + 98 + private function buildBindingList(AlmanacService $service) { 99 + $viewer = $this->getViewer(); 100 + $id = $service->getID(); 101 + 102 + $can_edit = PhabricatorPolicyFilter::hasCapability( 103 + $viewer, 104 + $service, 105 + PhabricatorPolicyCapability::CAN_EDIT); 106 + 107 + $bindings = id(new AlmanacBindingQuery()) 108 + ->setViewer($viewer) 109 + ->withServicePHIDs(array($service->getPHID())) 110 + ->execute(); 111 + 112 + $phids = array(); 113 + foreach ($bindings as $binding) { 114 + $phids[] = $binding->getServicePHID(); 115 + $phids[] = $binding->getDevicePHID(); 116 + $phids[] = $binding->getInterface()->getNetworkPHID(); 117 + } 118 + $handles = $this->loadViewerHandles($phids); 119 + 120 + $table = id(new AlmanacBindingTableView()) 121 + ->setNoDataString( 122 + pht('This service has not been bound to any device interfaces yet.')) 123 + ->setUser($viewer) 124 + ->setBindings($bindings) 125 + ->setHandles($handles); 126 + 127 + $header = id(new PHUIHeaderView()) 128 + ->setHeader(pht('Service Bindings')) 129 + ->addActionLink( 130 + id(new PHUIButtonView()) 131 + ->setTag('a') 132 + ->setHref($this->getApplicationURI("binding/edit/?serviceID={$id}")) 133 + ->setWorkflow(!$can_edit) 134 + ->setDisabled(!$can_edit) 135 + ->setText(pht('Add Binding')) 136 + ->setIcon( 137 + id(new PHUIIconView()) 138 + ->setIconFont('fa-plus'))); 139 + 140 + return id(new PHUIObjectBoxView()) 141 + ->setHeader($header) 142 + ->appendChild($table); 93 143 } 94 144 95 145 }
+139
src/applications/almanac/editor/AlmanacBindingEditor.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorAlmanacApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Almanac Binding'); 12 + } 13 + 14 + public function getTransactionTypes() { 15 + $types = parent::getTransactionTypes(); 16 + 17 + $types[] = AlmanacBindingTransaction::TYPE_INTERFACE; 18 + 19 + return $types; 20 + } 21 + 22 + protected function getCustomTransactionOldValue( 23 + PhabricatorLiskDAO $object, 24 + PhabricatorApplicationTransaction $xaction) { 25 + switch ($xaction->getTransactionType()) { 26 + case AlmanacBindingTransaction::TYPE_INTERFACE: 27 + return $object->getInterfacePHID(); 28 + } 29 + 30 + return parent::getCustomTransactionOldValue($object, $xaction); 31 + } 32 + 33 + protected function getCustomTransactionNewValue( 34 + PhabricatorLiskDAO $object, 35 + PhabricatorApplicationTransaction $xaction) { 36 + 37 + switch ($xaction->getTransactionType()) { 38 + case AlmanacBindingTransaction::TYPE_INTERFACE: 39 + return $xaction->getNewValue(); 40 + } 41 + 42 + return parent::getCustomTransactionNewValue($object, $xaction); 43 + } 44 + 45 + protected function applyCustomInternalTransaction( 46 + PhabricatorLiskDAO $object, 47 + PhabricatorApplicationTransaction $xaction) { 48 + 49 + switch ($xaction->getTransactionType()) { 50 + case AlmanacBindingTransaction::TYPE_INTERFACE: 51 + $interface = id(new AlmanacInterfaceQuery()) 52 + ->setViewer($this->requireActor()) 53 + ->withPHIDs(array($xaction->getNewValue())) 54 + ->executeOne(); 55 + $object->setDevicePHID($interface->getDevicePHID()); 56 + $object->setInterfacePHID($interface->getPHID()); 57 + return; 58 + } 59 + 60 + return parent::applyCustomInternalTransaction($object, $xaction); 61 + } 62 + 63 + protected function applyCustomExternalTransaction( 64 + PhabricatorLiskDAO $object, 65 + PhabricatorApplicationTransaction $xaction) { 66 + 67 + switch ($xaction->getTransactionType()) { 68 + case AlmanacBindingTransaction::TYPE_INTERFACE: 69 + return; 70 + } 71 + 72 + return parent::applyCustomExternalTransaction($object, $xaction); 73 + } 74 + 75 + protected function validateTransaction( 76 + PhabricatorLiskDAO $object, 77 + $type, 78 + array $xactions) { 79 + 80 + $errors = parent::validateTransaction($object, $type, $xactions); 81 + 82 + switch ($type) { 83 + case AlmanacBindingTransaction::TYPE_INTERFACE: 84 + $missing = $this->validateIsEmptyTextField( 85 + $object->getInterfacePHID(), 86 + $xactions); 87 + if ($missing) { 88 + $error = new PhabricatorApplicationTransactionValidationError( 89 + $type, 90 + pht('Required'), 91 + pht('Bindings must specify an interface.'), 92 + nonempty(last($xactions), null)); 93 + $error->setIsMissingFieldError(true); 94 + $errors[] = $error; 95 + } else if ($xactions) { 96 + foreach ($xactions as $xaction) { 97 + $interfaces = id(new AlmanacInterfaceQuery()) 98 + ->setViewer($this->requireActor()) 99 + ->withPHIDs(array($xaction->getNewValue())) 100 + ->execute(); 101 + if (!$interfaces) { 102 + $error = new PhabricatorApplicationTransactionValidationError( 103 + $type, 104 + pht('Invalid'), 105 + pht( 106 + 'You can not bind a service to an invalid or restricted '. 107 + 'interface.'), 108 + $xaction); 109 + $errors[] = $error; 110 + } 111 + } 112 + 113 + $final_value = last($xactions)->getNewValue(); 114 + 115 + $binding = id(new AlmanacBindingQuery()) 116 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 117 + ->withServicePHIDs(array($object->getServicePHID())) 118 + ->withInterfacePHIDs(array($final_value)) 119 + ->executeOne(); 120 + if ($binding && ($binding->getID() != $object->getID())) { 121 + $error = new PhabricatorApplicationTransactionValidationError( 122 + $type, 123 + pht('Already Bound'), 124 + pht( 125 + 'You can not bind a service to the same interface multiple '. 126 + 'times.'), 127 + last($xactions)); 128 + $errors[] = $error; 129 + } 130 + } 131 + break; 132 + } 133 + 134 + return $errors; 135 + } 136 + 137 + 138 + 139 + }
+38
src/applications/almanac/phid/AlmanacBindingPHIDType.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingPHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'ABND'; 6 + 7 + public function getTypeName() { 8 + return pht('Almanac Binding'); 9 + } 10 + 11 + public function newObject() { 12 + return new AlmanacBinding(); 13 + } 14 + 15 + protected function buildQueryForObjects( 16 + PhabricatorObjectQuery $query, 17 + array $phids) { 18 + 19 + return id(new AlmanacBindingQuery()) 20 + ->withPHIDs($phids); 21 + } 22 + 23 + public function loadHandles( 24 + PhabricatorHandleQuery $query, 25 + array $handles, 26 + array $objects) { 27 + 28 + foreach ($handles as $phid => $handle) { 29 + $binding = $objects[$phid]; 30 + 31 + $id = $binding->getID(); 32 + 33 + $handle->setObjectName(pht('Binding %d', $id)); 34 + $handle->setName(pht('Binding %d', $id)); 35 + } 36 + } 37 + 38 + }
+1
src/applications/almanac/phid/AlmanacDevicePHIDType.php
··· 33 33 34 34 $handle->setObjectName(pht('Device %d', $id)); 35 35 $handle->setName($name); 36 + $handle->setURI($device->getURI()); 36 37 } 37 38 } 38 39
+13 -1
src/applications/almanac/phid/AlmanacInterfacePHIDType.php
··· 30 30 31 31 $id = $interface->getID(); 32 32 33 + $device_name = $interface->getDevice()->getName(); 34 + $address = $interface->getAddress(); 35 + $port = $interface->getPort(); 36 + $network = $interface->getNetwork()->getName(); 37 + 38 + $name = pht( 39 + '%s:%s (%s on %s)', 40 + $device_name, 41 + $port, 42 + $address, 43 + $network); 44 + 33 45 $handle->setObjectName(pht('Interface %d', $id)); 34 - $handle->setName(pht('Interface %d', $id)); 46 + $handle->setName($name); 35 47 } 36 48 } 37 49
+1
src/applications/almanac/phid/AlmanacNetworkPHIDType.php
··· 33 33 34 34 $handle->setObjectName(pht('Network %d', $id)); 35 35 $handle->setName($name); 36 + $handle->setURI($network->getURI()); 36 37 } 37 38 } 38 39
+1
src/applications/almanac/phid/AlmanacServicePHIDType.php
··· 33 33 34 34 $handle->setObjectName(pht('Service %d', $id)); 35 35 $handle->setName($name); 36 + $handle->setURI($service->getURI()); 36 37 } 37 38 } 38 39
+143
src/applications/almanac/query/AlmanacBindingQuery.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $servicePHIDs; 9 + private $devicePHIDs; 10 + private $interfacePHIDs; 11 + 12 + public function withIDs(array $ids) { 13 + $this->ids = $ids; 14 + return $this; 15 + } 16 + 17 + public function withPHIDs(array $phids) { 18 + $this->phids = $phids; 19 + return $this; 20 + } 21 + 22 + public function withServicePHIDs(array $phids) { 23 + $this->servicePHIDs = $phids; 24 + return $this; 25 + } 26 + 27 + public function withDevicePHIDs(array $phids) { 28 + $this->devicePHIDs = $phids; 29 + return $this; 30 + } 31 + 32 + public function withInterfacePHIDs(array $phids) { 33 + $this->interfacePHIDs = $phids; 34 + return $this; 35 + } 36 + 37 + protected function loadPage() { 38 + $table = new AlmanacBinding(); 39 + $conn_r = $table->establishConnection('r'); 40 + 41 + $data = queryfx_all( 42 + $conn_r, 43 + 'SELECT * FROM %T %Q %Q %Q', 44 + $table->getTableName(), 45 + $this->buildWhereClause($conn_r), 46 + $this->buildOrderClause($conn_r), 47 + $this->buildLimitClause($conn_r)); 48 + 49 + return $table->loadAllFromArray($data); 50 + } 51 + 52 + protected function willFilterPage(array $bindings) { 53 + $service_phids = mpull($bindings, 'getServicePHID'); 54 + $device_phids = mpull($bindings, 'getDevicePHID'); 55 + $interface_phids = mpull($bindings, 'getInterfacePHID'); 56 + 57 + $services = id(new AlmanacServiceQuery()) 58 + ->setParentQuery($this) 59 + ->setViewer($this->getViewer()) 60 + ->withPHIDs($service_phids) 61 + ->execute(); 62 + $services = mpull($services, null, 'getPHID'); 63 + 64 + $devices = id(new AlmanacDeviceQuery()) 65 + ->setParentQuery($this) 66 + ->setViewer($this->getViewer()) 67 + ->withPHIDs($device_phids) 68 + ->execute(); 69 + $devices = mpull($devices, null, 'getPHID'); 70 + 71 + $interfaces = id(new AlmanacInterfaceQuery()) 72 + ->setParentQuery($this) 73 + ->setViewer($this->getViewer()) 74 + ->withPHIDs($interface_phids) 75 + ->execute(); 76 + $interfaces = mpull($interfaces, null, 'getPHID'); 77 + 78 + foreach ($bindings as $key => $binding) { 79 + $service = idx($services, $binding->getServicePHID()); 80 + $device = idx($devices, $binding->getDevicePHID()); 81 + $interface = idx($interfaces, $binding->getInterfacePHID()); 82 + if (!$service || !$device || !$interface) { 83 + $this->didRejectResult($binding); 84 + unset($bindings[$key]); 85 + continue; 86 + } 87 + 88 + $binding->attachService($service); 89 + $binding->attachDevice($device); 90 + $binding->attachInterface($interface); 91 + } 92 + 93 + return $bindings; 94 + } 95 + 96 + protected function buildWhereClause($conn_r) { 97 + $where = array(); 98 + 99 + if ($this->ids !== null) { 100 + $where[] = qsprintf( 101 + $conn_r, 102 + 'id IN (%Ld)', 103 + $this->ids); 104 + } 105 + 106 + if ($this->phids !== null) { 107 + $where[] = qsprintf( 108 + $conn_r, 109 + 'phid IN (%Ls)', 110 + $this->phids); 111 + } 112 + 113 + if ($this->servicePHIDs !== null) { 114 + $where[] = qsprintf( 115 + $conn_r, 116 + 'servicePHID IN (%Ls)', 117 + $this->servicePHIDs); 118 + } 119 + 120 + if ($this->devicePHIDs !== null) { 121 + $where[] = qsprintf( 122 + $conn_r, 123 + 'devicePHID IN (%Ls)', 124 + $this->devicePHIDs); 125 + } 126 + 127 + if ($this->interfacePHIDs !== null) { 128 + $where[] = qsprintf( 129 + $conn_r, 130 + 'interfacePHID IN (%Ls)', 131 + $this->interfacePHIDs); 132 + } 133 + 134 + $where[] = $this->buildPagingClause($conn_r); 135 + 136 + return $this->formatWhereClause($where); 137 + } 138 + 139 + public function getQueryApplicationClass() { 140 + return 'PhabricatorAlmanacApplication'; 141 + } 142 + 143 + }
+10
src/applications/almanac/query/AlmanacBindingTransactionQuery.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new AlmanacBindingTransaction(); 8 + } 9 + 10 + }
+13
src/applications/almanac/query/AlmanacDeviceQuery.php
··· 6 6 private $ids; 7 7 private $phids; 8 8 private $names; 9 + private $datasourceQuery; 9 10 10 11 public function withIDs(array $ids) { 11 12 $this->ids = $ids; ··· 19 20 20 21 public function withNames(array $names) { 21 22 $this->names = $names; 23 + return $this; 24 + } 25 + 26 + public function withDatasourceQuery($query) { 27 + $this->datasourceQuery = $query; 22 28 return $this; 23 29 } 24 30 ··· 63 69 $conn_r, 64 70 'nameIndex IN (%Ls)', 65 71 $hashes); 72 + } 73 + 74 + if ($this->datasourceQuery !== null) { 75 + $where[] = qsprintf( 76 + $conn_r, 77 + 'name LIKE %>', 78 + $this->datasourceQuery); 66 79 } 67 80 68 81 $where[] = $this->buildPagingClause($conn_r);
+112
src/applications/almanac/storage/AlmanacBinding.php
··· 1 + <?php 2 + 3 + final class AlmanacBinding 4 + extends AlmanacDAO 5 + implements PhabricatorPolicyInterface { 6 + 7 + protected $servicePHID; 8 + protected $devicePHID; 9 + protected $interfacePHID; 10 + protected $mailKey; 11 + 12 + private $service = self::ATTACHABLE; 13 + private $device = self::ATTACHABLE; 14 + private $interface = self::ATTACHABLE; 15 + 16 + public static function initializeNewBinding(AlmanacService $service) { 17 + return id(new AlmanacBinding()) 18 + ->setServicePHID($service->getPHID()); 19 + } 20 + 21 + public function getConfiguration() { 22 + return array( 23 + self::CONFIG_AUX_PHID => true, 24 + self::CONFIG_COLUMN_SCHEMA => array( 25 + 'mailKey' => 'bytes20', 26 + ), 27 + self::CONFIG_KEY_SCHEMA => array( 28 + 'key_service' => array( 29 + 'columns' => array('servicePHID', 'interfacePHID'), 30 + 'unique' => true, 31 + ), 32 + 'key_device' => array( 33 + 'columns' => array('devicePHID'), 34 + ), 35 + 'key_interface' => array( 36 + 'columns' => array('interfacePHID'), 37 + ), 38 + ), 39 + ) + parent::getConfiguration(); 40 + } 41 + 42 + public function generatePHID() { 43 + return PhabricatorPHID::generateNewPHID(AlmanacBindingPHIDType::TYPECONST); 44 + } 45 + 46 + public function save() { 47 + if (!$this->mailKey) { 48 + $this->mailKey = Filesystem::readRandomCharacters(20); 49 + } 50 + return parent::save(); 51 + } 52 + 53 + public function getURI() { 54 + return '/almanac/binding/'.$this->getID().'/'; 55 + } 56 + 57 + public function getService() { 58 + return $this->assertAttached($this->service); 59 + } 60 + 61 + public function attachService(AlmanacService $service) { 62 + $this->service = $service; 63 + return $this; 64 + } 65 + 66 + public function getDevice() { 67 + return $this->assertAttached($this->device); 68 + } 69 + 70 + public function attachDevice(AlmanacDevice $device) { 71 + $this->device = $device; 72 + return $this; 73 + } 74 + 75 + public function getInterface() { 76 + return $this->assertAttached($this->interface); 77 + } 78 + 79 + public function attachInterface(AlmanacInterface $interface) { 80 + $this->interface = $interface; 81 + return $this; 82 + } 83 + 84 + 85 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 86 + 87 + 88 + public function getCapabilities() { 89 + return array( 90 + PhabricatorPolicyCapability::CAN_VIEW, 91 + PhabricatorPolicyCapability::CAN_EDIT, 92 + ); 93 + } 94 + 95 + public function getPolicy($capability) { 96 + return $this->getService()->getPolicy($capability); 97 + } 98 + 99 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 100 + return $this->getService()->hasAutomaticCapability($capability, $viewer); 101 + } 102 + 103 + public function describeAutomaticCapability($capability) { 104 + return array( 105 + pht('A binding inherits the policies of its service.'), 106 + pht( 107 + 'To view a binding, you must also be able to view its device and '. 108 + 'interface.'), 109 + ); 110 + } 111 + 112 + }
+65
src/applications/almanac/storage/AlmanacBindingTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_INTERFACE = 'almanac:binding:interface'; 7 + 8 + public function getApplicationName() { 9 + return 'almanac'; 10 + } 11 + 12 + public function getApplicationTransactionType() { 13 + return AlmanacBindingPHIDType::TYPECONST; 14 + } 15 + 16 + public function getApplicationTransactionCommentObject() { 17 + return null; 18 + } 19 + 20 + public function getRequiredHandlePHIDs() { 21 + $phids = parent::getRequiredHandlePHIDs(); 22 + 23 + $old = $this->getOldValue(); 24 + $new = $this->getNewValue(); 25 + 26 + switch ($this->getTransactionType()) { 27 + case self::TYPE_INTERFACE: 28 + if ($old) { 29 + $phids[] = $old; 30 + } 31 + if ($new) { 32 + $phids[] = $new; 33 + } 34 + break; 35 + } 36 + 37 + return $phids; 38 + } 39 + 40 + public function getTitle() { 41 + $author_phid = $this->getAuthorPHID(); 42 + 43 + $old = $this->getOldValue(); 44 + $new = $this->getNewValue(); 45 + 46 + switch ($this->getTransactionType()) { 47 + case self::TYPE_INTERFACE: 48 + if ($old === null) { 49 + return pht( 50 + '%s created this binding.', 51 + $this->renderHandleLink($author_phid)); 52 + } else { 53 + return pht( 54 + '%s changed this binding from %s to %s.', 55 + $this->renderHandleLink($author_phid), 56 + $this->renderHandleLink($old), 57 + $this->renderHandleLink($new)); 58 + } 59 + break; 60 + } 61 + 62 + return parent::getTitle(); 63 + } 64 + 65 + }
+6 -2
src/applications/almanac/storage/AlmanacInterface.php
··· 52 52 return $this->assertAttached($this->network); 53 53 } 54 54 55 - public function attachNetwork(AlmanacNetwork $device) { 56 - $this->device = $device; 55 + public function attachNetwork(AlmanacNetwork $network) { 56 + $this->network = $network; 57 57 return $this; 58 58 } 59 59 ··· 66 66 67 67 public function getAddressHash() { 68 68 return $this->toAddress()->toHash(); 69 + } 70 + 71 + public function renderDisplayAddress() { 72 + return $this->getAddress().':'.$this->getPort(); 69 73 } 70 74 71 75
+51
src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getPlaceholderText() { 7 + return pht('Type an interface name...'); 8 + } 9 + 10 + public function getDatasourceApplicationClass() { 11 + return 'PhabricatorAlmanacApplication'; 12 + } 13 + 14 + public function loadResults() { 15 + $viewer = $this->getViewer(); 16 + $raw_query = $this->getRawQuery(); 17 + 18 + $devices = id(new AlmanacDeviceQuery()) 19 + ->setViewer($viewer) 20 + ->withDatasourceQuery($raw_query) 21 + ->execute(); 22 + 23 + if ($devices) { 24 + $interfaces = id(new AlmanacInterfaceQuery()) 25 + ->setViewer($viewer) 26 + ->withDevicePHIDs(mpull($devices, 'getPHID')) 27 + ->execute(); 28 + } else { 29 + $interfaces = array(); 30 + } 31 + 32 + if ($interfaces) { 33 + $handles = id(new PhabricatorHandleQuery()) 34 + ->setViewer($viewer) 35 + ->withPHIDs(mpull($interfaces, 'getPHID')) 36 + ->execute(); 37 + } else { 38 + $handles = array(); 39 + } 40 + 41 + $results = array(); 42 + foreach ($handles as $handle) { 43 + $results[] = id(new PhabricatorTypeaheadResult()) 44 + ->setName($handle->getName()) 45 + ->setPHID($handle->getPHID()); 46 + } 47 + 48 + return $results; 49 + } 50 + 51 + }
+86
src/applications/almanac/view/AlmanacBindingTableView.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingTableView extends AphrontView { 4 + 5 + private $bindings; 6 + private $handles; 7 + private $noDataString; 8 + 9 + public function setNoDataString($no_data_string) { 10 + $this->noDataString = $no_data_string; 11 + return $this; 12 + } 13 + 14 + public function getNoDataString() { 15 + return $this->noDataString; 16 + } 17 + 18 + public function setHandles(array $handles) { 19 + $this->handles = $handles; 20 + return $this; 21 + } 22 + 23 + public function getHandles() { 24 + return $this->handles; 25 + } 26 + 27 + public function setBindings(array $bindings) { 28 + $this->bindings = $bindings; 29 + return $this; 30 + } 31 + 32 + public function getBindings() { 33 + return $this->bindings; 34 + } 35 + 36 + public function render() { 37 + $bindings = $this->getBindings(); 38 + $handles = $this->getHandles(); 39 + $viewer = $this->getUser(); 40 + 41 + $rows = array(); 42 + foreach ($bindings as $binding) { 43 + $addr = $binding->getInterface()->getAddress(); 44 + $port = $binding->getInterface()->getPort(); 45 + 46 + $rows[] = array( 47 + $binding->getID(), 48 + $handles[$binding->getServicePHID()]->renderLink(), 49 + $handles[$binding->getDevicePHID()]->renderLink(), 50 + $handles[$binding->getInterface()->getNetworkPHID()]->renderLink(), 51 + $binding->getInterface()->renderDisplayAddress(), 52 + phutil_tag( 53 + 'a', 54 + array( 55 + 'class' => 'small grey button', 56 + 'href' => '/almanac/binding/'.$binding->getID().'/', 57 + ), 58 + pht('Details')), 59 + ); 60 + } 61 + 62 + $table = id(new AphrontTableView($rows)) 63 + ->setNoDataString($this->getNoDataString()) 64 + ->setHeaders( 65 + array( 66 + pht('ID'), 67 + pht('Service'), 68 + pht('Device'), 69 + pht('Network'), 70 + pht('Interface'), 71 + null, 72 + )) 73 + ->setColumnClasses( 74 + array( 75 + '', 76 + '', 77 + '', 78 + '', 79 + 'wide', 80 + 'action', 81 + )); 82 + 83 + return $table; 84 + } 85 + 86 + }