@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.

Allow Almanac Devices and Bindings to have properties

Summary:
Ref T5833. Adds support for arbitrary properites to Almanac devices and bindings.

- For Devices, this allows you to maybe mark what `rack` a server is on, the `serial` number of a router, etc.
- For Bindings, this allows you to maybe mark that a bound device is `active`, provide `credentials`, expose it as `readonly`, etc.

Test Plan: Added properties to Devices and Bindings.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5833

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

+249 -61
+11 -3
src/__phutil_library_map__.php
··· 65 65 'AlmanacPropertyEditController' => 'applications/almanac/controller/AlmanacPropertyEditController.php', 66 66 'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php', 67 67 'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php', 68 + 'AlmanacQuery' => 'applications/almanac/query/AlmanacQuery.php', 68 69 'AlmanacService' => 'applications/almanac/storage/AlmanacService.php', 69 70 'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php', 70 71 'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php', ··· 2972 2973 'AlmanacBinding' => array( 2973 2974 'AlmanacDAO', 2974 2975 'PhabricatorPolicyInterface', 2976 + 'PhabricatorCustomFieldInterface', 2977 + 'PhabricatorApplicationTransactionInterface', 2978 + 'AlmanacPropertyInterface', 2975 2979 ), 2976 2980 'AlmanacBindingEditController' => 'AlmanacServiceController', 2977 2981 'AlmanacBindingEditor' => 'PhabricatorApplicationTransactionEditor', 2978 2982 'AlmanacBindingPHIDType' => 'PhabricatorPHIDType', 2979 - 'AlmanacBindingQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2983 + 'AlmanacBindingQuery' => 'AlmanacQuery', 2980 2984 'AlmanacBindingTableView' => 'AphrontView', 2981 2985 'AlmanacBindingTransaction' => 'PhabricatorApplicationTransaction', 2982 2986 'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery', ··· 2996 3000 'AlmanacDevice' => array( 2997 3001 'AlmanacDAO', 2998 3002 'PhabricatorPolicyInterface', 3003 + 'PhabricatorCustomFieldInterface', 3004 + 'PhabricatorApplicationTransactionInterface', 3005 + 'AlmanacPropertyInterface', 2999 3006 ), 3000 3007 'AlmanacDeviceController' => 'AlmanacController', 3001 3008 'AlmanacDeviceEditController' => 'AlmanacDeviceController', 3002 3009 'AlmanacDeviceEditor' => 'PhabricatorApplicationTransactionEditor', 3003 3010 'AlmanacDeviceListController' => 'AlmanacDeviceController', 3004 3011 'AlmanacDevicePHIDType' => 'PhabricatorPHIDType', 3005 - 'AlmanacDeviceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3012 + 'AlmanacDeviceQuery' => 'AlmanacQuery', 3006 3013 'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine', 3007 3014 'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction', 3008 3015 'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', ··· 3041 3048 'AlmanacPropertyController' => 'AlmanacController', 3042 3049 'AlmanacPropertyEditController' => 'AlmanacDeviceController', 3043 3050 'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3051 + 'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3044 3052 'AlmanacService' => array( 3045 3053 'AlmanacDAO', 3046 3054 'PhabricatorPolicyInterface', ··· 3053 3061 'AlmanacServiceEditor' => 'PhabricatorApplicationTransactionEditor', 3054 3062 'AlmanacServiceListController' => 'AlmanacServiceController', 3055 3063 'AlmanacServicePHIDType' => 'PhabricatorPHIDType', 3056 - 'AlmanacServiceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3064 + 'AlmanacServiceQuery' => 'AlmanacQuery', 3057 3065 'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine', 3058 3066 'AlmanacServiceTransaction' => 'PhabricatorApplicationTransaction', 3059 3067 'AlmanacServiceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+1
src/applications/almanac/controller/AlmanacBindingViewController.php
··· 57 57 array( 58 58 $crumbs, 59 59 $box, 60 + $this->buildAlmanacPropertiesTable($binding), 60 61 $xaction_view, 61 62 ), 62 63 array(
+3 -6
src/applications/almanac/controller/AlmanacController.php
··· 3 3 abstract class AlmanacController 4 4 extends PhabricatorController { 5 5 6 + protected function buildAlmanacPropertiesTable( 7 + AlmanacPropertyInterface $object) { 6 8 7 - protected function buildAlmanacPropertiesTable($object) { 8 9 $viewer = $this->getViewer(); 9 - 10 - $properties = id(new AlmanacPropertyQuery()) 11 - ->setViewer($viewer) 12 - ->withObjectPHIDs(array($object->getPHID())) 13 - ->execute(); 10 + $properties = $object->getAlmanacProperties(); 14 11 15 12 $rows = array(); 16 13 foreach ($properties as $property) {
+1
src/applications/almanac/controller/AlmanacDeviceViewController.php
··· 56 56 $crumbs, 57 57 $box, 58 58 $interfaces, 59 + $this->buildAlmanacPropertiesTable($device), 59 60 $xaction_view, 60 61 ), 61 62 array(
-1
src/applications/almanac/controller/AlmanacServiceViewController.php
··· 15 15 $service = id(new AlmanacServiceQuery()) 16 16 ->setViewer($viewer) 17 17 ->withNames(array($name)) 18 - ->needProperties(true) 19 18 ->executeOne(); 20 19 if (!$service) { 21 20 return new Aphront404Response();
+1 -5
src/applications/almanac/query/AlmanacBindingQuery.php
··· 1 1 <?php 2 2 3 3 final class AlmanacBindingQuery 4 - extends PhabricatorCursorPagedPolicyAwareQuery { 4 + extends AlmanacQuery { 5 5 6 6 private $ids; 7 7 private $phids; ··· 134 134 $where[] = $this->buildPagingClause($conn_r); 135 135 136 136 return $this->formatWhereClause($where); 137 - } 138 - 139 - public function getQueryApplicationClass() { 140 - return 'PhabricatorAlmanacApplication'; 141 137 } 142 138 143 139 }
+1 -1
src/applications/almanac/query/AlmanacDeviceQuery.php
··· 1 1 <?php 2 2 3 3 final class AlmanacDeviceQuery 4 - extends PhabricatorCursorPagedPolicyAwareQuery { 4 + extends AlmanacQuery { 5 5 6 6 private $ids; 7 7 private $phids;
+38 -13
src/applications/almanac/query/AlmanacPropertyQuery.php
··· 3 3 final class AlmanacPropertyQuery 4 4 extends PhabricatorCursorPagedPolicyAwareQuery { 5 5 6 + private $ids; 6 7 private $objectPHIDs; 7 8 private $names; 9 + private $disablePolicyFilteringAndAttachment; 10 + 11 + public function withIDs(array $ids) { 12 + $this->ids = $ids; 13 + return $this; 14 + } 8 15 9 16 public function withObjectPHIDs(array $phids) { 10 17 $this->phids = $phids; ··· 16 23 return $this; 17 24 } 18 25 26 + public function setDisablePolicyFilteringAndAttachment($disable) { 27 + $this->disablePolicyFilteringAndAttachment = $disable; 28 + return $this; 29 + } 30 + 31 + protected function shouldDisablePolicyFiltering() { 32 + return $this->disablePolicyFilteringAndAttachment; 33 + } 34 + 19 35 protected function loadPage() { 20 36 $table = new AlmanacProperty(); 21 37 $conn_r = $table->establishConnection('r'); ··· 32 48 } 33 49 34 50 protected function willFilterPage(array $properties) { 35 - $object_phids = mpull($properties, 'getObjectPHID'); 51 + if (!$this->disablePolicyFilteringAndAttachment) { 52 + $object_phids = mpull($properties, 'getObjectPHID'); 36 53 37 - $objects = id(new PhabricatorObjectQuery()) 38 - ->setViewer($this->getViewer()) 39 - ->setParentQuery($this) 40 - ->withPHIDs($object_phids) 41 - ->execute(); 42 - $objects = mpull($objects, null, 'getPHID'); 54 + $objects = id(new PhabricatorObjectQuery()) 55 + ->setViewer($this->getViewer()) 56 + ->setParentQuery($this) 57 + ->withPHIDs($object_phids) 58 + ->execute(); 59 + $objects = mpull($objects, null, 'getPHID'); 43 60 44 - foreach ($properties as $key => $property) { 45 - $object = idx($objects, $property->getObjectPHID()); 46 - if (!$object) { 47 - unset($properties[$key]); 48 - continue; 61 + foreach ($properties as $key => $property) { 62 + $object = idx($objects, $property->getObjectPHID()); 63 + if (!$object) { 64 + unset($properties[$key]); 65 + continue; 66 + } 67 + $property->attachObject($object); 49 68 } 50 - $property->attachObject($object); 51 69 } 52 70 53 71 return $properties; ··· 55 73 56 74 protected function buildWhereClause($conn_r) { 57 75 $where = array(); 76 + 77 + if ($this->ids !== null) { 78 + $where[] = qsprintf( 79 + $conn_r, 80 + 'id IN (%Ld)', 81 + $this->ids); 82 + } 58 83 59 84 if ($this->objectPHIDs !== null) { 60 85 $where[] = qsprintf(
+41
src/applications/almanac/query/AlmanacQuery.php
··· 1 + <?php 2 + 3 + abstract class AlmanacQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + protected function didFilterPage(array $objects) { 7 + if (head($objects) instanceof AlmanacPropertyInterface) { 8 + // NOTE: We load properties unconditionally because CustomField assumes 9 + // it can always generate a list of fields on an object. It may make 10 + // sense to re-examine that assumption eventually. 11 + 12 + $property_query = id(new AlmanacPropertyQuery()) 13 + ->setViewer($this->getViewer()) 14 + ->setParentQuery($this) 15 + ->withObjectPHIDs(mpull($objects, null, 'getPHID')); 16 + 17 + // NOTE: We disable policy filtering and object attachment to avoid 18 + // a cyclic dependency where objects need their properties and properties 19 + // need their objects. We'll attach the objects below, and have already 20 + // implicitly checked the necessary policies. 21 + $property_query->setDisablePolicyFilteringAndAttachment(true); 22 + 23 + $properties = $property_query->execute(); 24 + $properties = mgroup($properties, 'getObjectPHID'); 25 + foreach ($objects as $object) { 26 + $object_properties = idx($properties, $object->getPHID(), array()); 27 + foreach ($object_properties as $property) { 28 + $property->attachObject($object); 29 + } 30 + $object->attachAlmanacProperties($object_properties); 31 + } 32 + } 33 + 34 + return $objects; 35 + } 36 + 37 + public function getQueryApplicationClass() { 38 + return 'PhabricatorAlmanacApplication'; 39 + } 40 + 41 + }
+1 -30
src/applications/almanac/query/AlmanacServiceQuery.php
··· 1 1 <?php 2 2 3 3 final class AlmanacServiceQuery 4 - extends PhabricatorCursorPagedPolicyAwareQuery { 4 + extends AlmanacQuery { 5 5 6 6 private $ids; 7 7 private $phids; 8 8 private $names; 9 - private $needProperties; 10 9 11 10 public function withIDs(array $ids) { 12 11 $this->ids = $ids; ··· 20 19 21 20 public function withNames(array $names) { 22 21 $this->names = $names; 23 - return $this; 24 - } 25 - 26 - public function needProperties($need) { 27 - $this->needProperties = $need; 28 22 return $this; 29 23 } 30 24 ··· 75 69 $where[] = $this->buildPagingClause($conn_r); 76 70 77 71 return $this->formatWhereClause($where); 78 - } 79 - 80 - protected function didFilterPage(array $services) { 81 - // NOTE: We load properties unconditionally because CustomField assumes 82 - // it can always generate a list of fields on an object. It may make 83 - // sense to re-examine that assumption eventually. 84 - 85 - $properties = id(new AlmanacPropertyQuery()) 86 - ->setViewer($this->getViewer()) 87 - ->setParentQuery($this) 88 - ->withObjectPHIDs(mpull($services, null, 'getPHID')) 89 - ->execute(); 90 - $properties = mgroup($properties, 'getObjectPHID'); 91 - foreach ($services as $service) { 92 - $service_properties = idx($properties, $service->getPHID(), array()); 93 - $service->attachAlmanacProperties($service_properties); 94 - } 95 - 96 - return $services; 97 - } 98 - 99 - public function getQueryApplicationClass() { 100 - return 'PhabricatorAlmanacApplication'; 101 72 } 102 73 103 74 }
+75 -1
src/applications/almanac/storage/AlmanacBinding.php
··· 2 2 3 3 final class AlmanacBinding 4 4 extends AlmanacDAO 5 - implements PhabricatorPolicyInterface { 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorCustomFieldInterface, 8 + PhabricatorApplicationTransactionInterface, 9 + AlmanacPropertyInterface { 6 10 7 11 protected $servicePHID; 8 12 protected $devicePHID; ··· 12 16 private $service = self::ATTACHABLE; 13 17 private $device = self::ATTACHABLE; 14 18 private $interface = self::ATTACHABLE; 19 + private $customFields = self::ATTACHABLE; 20 + private $almanacProperties = self::ATTACHABLE; 15 21 16 22 public static function initializeNewBinding(AlmanacService $service) { 17 23 return id(new AlmanacBinding()) ··· 82 88 } 83 89 84 90 91 + /* -( AlmanacPropertyInterface )------------------------------------------- */ 92 + 93 + 94 + public function attachAlmanacProperties(array $properties) { 95 + assert_instances_of($properties, 'AlmanacProperty'); 96 + $this->almanacProperties = mpull($properties, null, 'getFieldName'); 97 + return $this; 98 + } 99 + 100 + public function getAlmanacProperties() { 101 + return $this->assertAttached($this->almanacProperties); 102 + } 103 + 104 + public function hasAlmanacProperty($key) { 105 + $this->assertAttached($this->almanacProperties); 106 + return isset($this->almanacProperties[$key]); 107 + } 108 + 109 + public function getAlmanacProperty($key) { 110 + return $this->assertAttachedKey($this->almanacProperties, $key); 111 + } 112 + 113 + public function getAlmanacPropertyValue($key, $default = null) { 114 + if ($this->hasAlmanacProperty($key)) { 115 + return $this->getAlmanacProperty($key)->getFieldValue(); 116 + } else { 117 + return $default; 118 + } 119 + } 120 + 121 + 85 122 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 86 123 87 124 ··· 107 144 'To view a binding, you must also be able to view its device and '. 108 145 'interface.'), 109 146 ); 147 + } 148 + 149 + 150 + /* -( PhabricatorCustomFieldInterface )------------------------------------ */ 151 + 152 + 153 + public function getCustomFieldSpecificationForRole($role) { 154 + return array(); 155 + } 156 + 157 + public function getCustomFieldBaseClass() { 158 + return 'AlmanacCustomField'; 159 + } 160 + 161 + public function getCustomFields() { 162 + return $this->assertAttached($this->customFields); 163 + } 164 + 165 + public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { 166 + $this->customFields = $fields; 167 + return $this; 168 + } 169 + 170 + 171 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 172 + 173 + 174 + public function getApplicationTransactionEditor() { 175 + return new AlmanacBindingEditor(); 176 + } 177 + 178 + public function getApplicationTransactionObject() { 179 + return $this; 180 + } 181 + 182 + public function getApplicationTransactionTemplate() { 183 + return new AlmanacBindingTransaction(); 110 184 } 111 185 112 186 }
+76 -1
src/applications/almanac/storage/AlmanacDevice.php
··· 2 2 3 3 final class AlmanacDevice 4 4 extends AlmanacDAO 5 - implements PhabricatorPolicyInterface { 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorCustomFieldInterface, 8 + PhabricatorApplicationTransactionInterface, 9 + AlmanacPropertyInterface { 6 10 7 11 protected $name; 8 12 protected $nameIndex; 9 13 protected $mailKey; 10 14 protected $viewPolicy; 11 15 protected $editPolicy; 16 + 17 + private $customFields = self::ATTACHABLE; 18 + private $almanacProperties = self::ATTACHABLE; 12 19 13 20 public static function initializeNewDevice() { 14 21 return id(new AlmanacDevice()) ··· 57 64 } 58 65 59 66 67 + /* -( AlmanacPropertyInterface )------------------------------------------- */ 68 + 69 + 70 + public function attachAlmanacProperties(array $properties) { 71 + assert_instances_of($properties, 'AlmanacProperty'); 72 + $this->almanacProperties = mpull($properties, null, 'getFieldName'); 73 + return $this; 74 + } 75 + 76 + public function getAlmanacProperties() { 77 + return $this->assertAttached($this->almanacProperties); 78 + } 79 + 80 + public function hasAlmanacProperty($key) { 81 + $this->assertAttached($this->almanacProperties); 82 + return isset($this->almanacProperties[$key]); 83 + } 84 + 85 + public function getAlmanacProperty($key) { 86 + return $this->assertAttachedKey($this->almanacProperties, $key); 87 + } 88 + 89 + public function getAlmanacPropertyValue($key, $default = null) { 90 + if ($this->hasAlmanacProperty($key)) { 91 + return $this->getAlmanacProperty($key)->getFieldValue(); 92 + } else { 93 + return $default; 94 + } 95 + } 96 + 97 + 60 98 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 61 99 62 100 ··· 82 120 83 121 public function describeAutomaticCapability($capability) { 84 122 return null; 123 + } 124 + 125 + 126 + /* -( PhabricatorCustomFieldInterface )------------------------------------ */ 127 + 128 + 129 + public function getCustomFieldSpecificationForRole($role) { 130 + return array(); 131 + } 132 + 133 + public function getCustomFieldBaseClass() { 134 + return 'AlmanacCustomField'; 135 + } 136 + 137 + public function getCustomFields() { 138 + return $this->assertAttached($this->customFields); 139 + } 140 + 141 + public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { 142 + $this->customFields = $fields; 143 + return $this; 144 + } 145 + 146 + 147 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 148 + 149 + 150 + public function getApplicationTransactionEditor() { 151 + return new AlmanacDeviceEditor(); 152 + } 153 + 154 + public function getApplicationTransactionObject() { 155 + return $this; 156 + } 157 + 158 + public function getApplicationTransactionTemplate() { 159 + return new AlmanacDeviceTransaction(); 85 160 } 86 161 87 162 }