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

Modularize Almanac Binding transactions

Summary: Depends on D19320. Ref T13120. Ref T12414. Move transactions for Almanac Bindings to ModularTransactions.

Test Plan:
- Created a new binding.
- Tried to create a duplicate binding, got an error.
- Edited a binding to rebind it to a different device.
- Disabled and enabled bindings.
- Grepped for `AlmanacBindingTransaction::` constants.

When a binding is created, it currently renders a bad "changed the interface from ??? to X" transaction. This is because creation isn't currently using EditEngine. I plan to swap it shortly, which will turn this into a real "Create" transaction and fix the issue.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13120, T12414

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

+159 -222
+7 -1
src/__phutil_library_map__.php
··· 13 13 'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php', 14 14 'AlmanacBinding' => 'applications/almanac/storage/AlmanacBinding.php', 15 15 'AlmanacBindingDisableController' => 'applications/almanac/controller/AlmanacBindingDisableController.php', 16 + 'AlmanacBindingDisableTransaction' => 'applications/almanac/xaction/AlmanacBindingDisableTransaction.php', 16 17 'AlmanacBindingEditController' => 'applications/almanac/controller/AlmanacBindingEditController.php', 17 18 'AlmanacBindingEditor' => 'applications/almanac/editor/AlmanacBindingEditor.php', 19 + 'AlmanacBindingInterfaceTransaction' => 'applications/almanac/xaction/AlmanacBindingInterfaceTransaction.php', 18 20 'AlmanacBindingPHIDType' => 'applications/almanac/phid/AlmanacBindingPHIDType.php', 19 21 'AlmanacBindingPropertyEditEngine' => 'applications/almanac/editor/AlmanacBindingPropertyEditEngine.php', 20 22 'AlmanacBindingQuery' => 'applications/almanac/query/AlmanacBindingQuery.php', 21 23 'AlmanacBindingTableView' => 'applications/almanac/view/AlmanacBindingTableView.php', 22 24 'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php', 23 25 'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php', 26 + 'AlmanacBindingTransactionType' => 'applications/almanac/xaction/AlmanacBindingTransactionType.php', 24 27 'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php', 25 28 'AlmanacBindingsSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php', 26 29 'AlmanacCacheEngineExtension' => 'applications/almanac/engineextension/AlmanacCacheEngineExtension.php', ··· 5179 5182 'PhabricatorExtendedPolicyInterface', 5180 5183 ), 5181 5184 'AlmanacBindingDisableController' => 'AlmanacServiceController', 5185 + 'AlmanacBindingDisableTransaction' => 'AlmanacBindingTransactionType', 5182 5186 'AlmanacBindingEditController' => 'AlmanacServiceController', 5183 5187 'AlmanacBindingEditor' => 'AlmanacEditor', 5188 + 'AlmanacBindingInterfaceTransaction' => 'AlmanacBindingTransactionType', 5184 5189 'AlmanacBindingPHIDType' => 'PhabricatorPHIDType', 5185 5190 'AlmanacBindingPropertyEditEngine' => 'AlmanacPropertyEditEngine', 5186 5191 'AlmanacBindingQuery' => 'AlmanacQuery', 5187 5192 'AlmanacBindingTableView' => 'AphrontView', 5188 - 'AlmanacBindingTransaction' => 'AlmanacTransaction', 5193 + 'AlmanacBindingTransaction' => 'PhabricatorModularTransaction', 5189 5194 'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 5195 + 'AlmanacBindingTransactionType' => 'AlmanacTransactionType', 5190 5196 'AlmanacBindingViewController' => 'AlmanacServiceController', 5191 5197 'AlmanacBindingsSearchEngineAttachment' => 'AlmanacSearchEngineAttachment', 5192 5198 'AlmanacCacheEngineExtension' => 'PhabricatorCacheEngineExtension',
+1 -1
src/applications/almanac/controller/AlmanacBindingDisableController.php
··· 40 40 41 41 42 42 if ($request->isFormPost()) { 43 - $type_disable = AlmanacBindingTransaction::TYPE_DISABLE; 43 + $type_disable = AlmanacBindingDisableTransaction::TRANSACTIONTYPE; 44 44 45 45 $xactions = array(); 46 46
+1 -1
src/applications/almanac/controller/AlmanacBindingEditController.php
··· 58 58 if ($request->isFormPost()) { 59 59 $v_interface = $request->getArr('interfacePHIDs'); 60 60 61 - $type_interface = AlmanacBindingTransaction::TYPE_INTERFACE; 61 + $type_interface = AlmanacBindingInterfaceTransaction::TRANSACTIONTYPE; 62 62 63 63 $xactions = array(); 64 64
+4 -161
src/applications/almanac/editor/AlmanacBindingEditor.php
··· 3 3 final class AlmanacBindingEditor 4 4 extends AlmanacEditor { 5 5 6 - private $devicePHID; 7 - 8 6 public function getEditorObjectsDescription() { 9 7 return pht('Almanac Binding'); 10 8 } 11 9 12 - public function getTransactionTypes() { 13 - $types = parent::getTransactionTypes(); 14 - 15 - $types[] = AlmanacBindingTransaction::TYPE_INTERFACE; 16 - $types[] = AlmanacBindingTransaction::TYPE_DISABLE; 17 - 18 - return $types; 19 - } 20 - 21 - protected function getCustomTransactionOldValue( 22 - PhabricatorLiskDAO $object, 23 - PhabricatorApplicationTransaction $xaction) { 24 - switch ($xaction->getTransactionType()) { 25 - case AlmanacBindingTransaction::TYPE_INTERFACE: 26 - return $object->getInterfacePHID(); 27 - case AlmanacBindingTransaction::TYPE_DISABLE: 28 - return $object->getIsDisabled(); 29 - } 30 - 31 - return parent::getCustomTransactionOldValue($object, $xaction); 10 + public function getCreateObjectTitle($author, $object) { 11 + return pht('%s created this binding.', $author); 32 12 } 33 13 34 - protected function getCustomTransactionNewValue( 35 - PhabricatorLiskDAO $object, 36 - PhabricatorApplicationTransaction $xaction) { 37 - 38 - switch ($xaction->getTransactionType()) { 39 - case AlmanacBindingTransaction::TYPE_INTERFACE: 40 - return $xaction->getNewValue(); 41 - case AlmanacBindingTransaction::TYPE_DISABLE: 42 - return (int)$xaction->getNewValue(); 43 - } 44 - 45 - return parent::getCustomTransactionNewValue($object, $xaction); 14 + public function getCreateObjectTitleForFeed($author, $object) { 15 + return pht('%s created %s.', $author, $object); 46 16 } 47 - 48 - protected function applyCustomInternalTransaction( 49 - PhabricatorLiskDAO $object, 50 - PhabricatorApplicationTransaction $xaction) { 51 - 52 - switch ($xaction->getTransactionType()) { 53 - case AlmanacBindingTransaction::TYPE_INTERFACE: 54 - $interface = id(new AlmanacInterfaceQuery()) 55 - ->setViewer($this->requireActor()) 56 - ->withPHIDs(array($xaction->getNewValue())) 57 - ->executeOne(); 58 - $object->setDevicePHID($interface->getDevicePHID()); 59 - $object->setInterfacePHID($interface->getPHID()); 60 - return; 61 - case AlmanacBindingTransaction::TYPE_DISABLE: 62 - $object->setIsDisabled($xaction->getNewValue()); 63 - return; 64 - } 65 - 66 - return parent::applyCustomInternalTransaction($object, $xaction); 67 - } 68 - 69 - protected function applyCustomExternalTransaction( 70 - PhabricatorLiskDAO $object, 71 - PhabricatorApplicationTransaction $xaction) { 72 - 73 - switch ($xaction->getTransactionType()) { 74 - case AlmanacBindingTransaction::TYPE_DISABLE: 75 - return; 76 - case AlmanacBindingTransaction::TYPE_INTERFACE: 77 - $interface_phids = array(); 78 - 79 - $interface_phids[] = $xaction->getOldValue(); 80 - $interface_phids[] = $xaction->getNewValue(); 81 - 82 - $interface_phids = array_filter($interface_phids); 83 - $interface_phids = array_unique($interface_phids); 84 - 85 - $interfaces = id(new AlmanacInterfaceQuery()) 86 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 87 - ->withPHIDs($interface_phids) 88 - ->execute(); 89 - 90 - $device_phids = array(); 91 - foreach ($interfaces as $interface) { 92 - $device_phids[] = $interface->getDevicePHID(); 93 - } 94 - 95 - $device_phids = array_unique($device_phids); 96 - 97 - $devices = id(new AlmanacDeviceQuery()) 98 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 99 - ->withPHIDs($device_phids) 100 - ->execute(); 101 - 102 - foreach ($devices as $device) { 103 - $device->rebuildClusterBindingStatus(); 104 - } 105 - return; 106 - } 107 - 108 - return parent::applyCustomExternalTransaction($object, $xaction); 109 - } 110 - 111 - protected function validateTransaction( 112 - PhabricatorLiskDAO $object, 113 - $type, 114 - array $xactions) { 115 - 116 - $errors = parent::validateTransaction($object, $type, $xactions); 117 - 118 - switch ($type) { 119 - case AlmanacBindingTransaction::TYPE_INTERFACE: 120 - $missing = $this->validateIsEmptyTextField( 121 - $object->getInterfacePHID(), 122 - $xactions); 123 - if ($missing) { 124 - $error = new PhabricatorApplicationTransactionValidationError( 125 - $type, 126 - pht('Required'), 127 - pht('Bindings must specify an interface.'), 128 - nonempty(last($xactions), null)); 129 - $error->setIsMissingFieldError(true); 130 - $errors[] = $error; 131 - } else if ($xactions) { 132 - foreach ($xactions as $xaction) { 133 - $interfaces = id(new AlmanacInterfaceQuery()) 134 - ->setViewer($this->requireActor()) 135 - ->withPHIDs(array($xaction->getNewValue())) 136 - ->execute(); 137 - if (!$interfaces) { 138 - $error = new PhabricatorApplicationTransactionValidationError( 139 - $type, 140 - pht('Invalid'), 141 - pht( 142 - 'You can not bind a service to an invalid or restricted '. 143 - 'interface.'), 144 - $xaction); 145 - $errors[] = $error; 146 - } 147 - } 148 - 149 - $final_value = last($xactions)->getNewValue(); 150 - 151 - $binding = id(new AlmanacBindingQuery()) 152 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 153 - ->withServicePHIDs(array($object->getServicePHID())) 154 - ->withInterfacePHIDs(array($final_value)) 155 - ->executeOne(); 156 - if ($binding && ($binding->getID() != $object->getID())) { 157 - $error = new PhabricatorApplicationTransactionValidationError( 158 - $type, 159 - pht('Already Bound'), 160 - pht( 161 - 'You can not bind a service to the same interface multiple '. 162 - 'times.'), 163 - last($xactions)); 164 - $errors[] = $error; 165 - } 166 - } 167 - break; 168 - } 169 - 170 - return $errors; 171 - } 172 - 173 - 174 17 175 18 }
+3 -58
src/applications/almanac/storage/AlmanacBindingTransaction.php
··· 1 1 <?php 2 2 3 3 final class AlmanacBindingTransaction 4 - extends AlmanacTransaction { 5 - 6 - const TYPE_INTERFACE = 'almanac:binding:interface'; 7 - const TYPE_DISABLE = 'almanac:binding:disable'; 4 + extends PhabricatorModularTransaction { 8 5 9 6 public function getApplicationName() { 10 7 return 'almanac'; ··· 18 15 return null; 19 16 } 20 17 21 - public function getRequiredHandlePHIDs() { 22 - $phids = parent::getRequiredHandlePHIDs(); 23 - 24 - $old = $this->getOldValue(); 25 - $new = $this->getNewValue(); 26 - 27 - switch ($this->getTransactionType()) { 28 - case self::TYPE_INTERFACE: 29 - if ($old) { 30 - $phids[] = $old; 31 - } 32 - if ($new) { 33 - $phids[] = $new; 34 - } 35 - break; 36 - } 37 - 38 - return $phids; 39 - } 40 - 41 - public function getTitle() { 42 - $author_phid = $this->getAuthorPHID(); 43 - 44 - $old = $this->getOldValue(); 45 - $new = $this->getNewValue(); 46 - 47 - switch ($this->getTransactionType()) { 48 - case self::TYPE_INTERFACE: 49 - if ($old === null) { 50 - return pht( 51 - '%s created this binding.', 52 - $this->renderHandleLink($author_phid)); 53 - } else { 54 - return pht( 55 - '%s changed this binding from %s to %s.', 56 - $this->renderHandleLink($author_phid), 57 - $this->renderHandleLink($old), 58 - $this->renderHandleLink($new)); 59 - } 60 - break; 61 - case self::TYPE_DISABLE: 62 - if ($new) { 63 - return pht( 64 - '%s disabled this binding.', 65 - $this->renderHandleLink($author_phid)); 66 - } else { 67 - return pht( 68 - '%s enabled this binding.', 69 - $this->renderHandleLink($author_phid)); 70 - } 71 - break; 72 - } 73 - 74 - return parent::getTitle(); 18 + public function getBaseTransactionClass() { 19 + return 'AlmanacBindingTransactionType'; 75 20 } 76 21 77 22 }
+28
src/applications/almanac/xaction/AlmanacBindingDisableTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingDisableTransaction 4 + extends AlmanacBindingTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:binding:disable'; 7 + 8 + public function generateOldValue($object) { 9 + return (bool)$object->getIsDisabled(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setIsDisabled((int)$value); 14 + } 15 + 16 + public function getTitle() { 17 + if ($this->getNewValue()) { 18 + return pht( 19 + '%s disabled this binding.', 20 + $this->renderAuthor()); 21 + } else { 22 + return pht( 23 + '%s enabled this binding.', 24 + $this->renderAuthor()); 25 + } 26 + } 27 + 28 + }
+111
src/applications/almanac/xaction/AlmanacBindingInterfaceTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacBindingInterfaceTransaction 4 + extends AlmanacBindingTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:binding:interface'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getInterfacePHID(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $interface = $this->loadInterface($value); 14 + 15 + $object 16 + ->setDevicePHID($interface->getDevicePHID()) 17 + ->setInterfacePHID($interface->getPHID()); 18 + } 19 + 20 + public function applyExternalEffects($object, $value) { 21 + 22 + // When we change which services a device is bound to, we need to 23 + // recalculate whether it is a cluster device or not so we can tell if 24 + // the "Can Manage Cluster Services" permission applies to it. 25 + 26 + $viewer = PhabricatorUser::getOmnipotentUser(); 27 + $interface_phids = array(); 28 + 29 + $interface_phids[] = $this->getOldValue(); 30 + $interface_phids[] = $this->getNewValue(); 31 + 32 + $interface_phids = array_filter($interface_phids); 33 + $interface_phids = array_unique($interface_phids); 34 + 35 + $interfaces = id(new AlmanacInterfaceQuery()) 36 + ->setViewer($viewer) 37 + ->withPHIDs($interface_phids) 38 + ->execute(); 39 + 40 + $device_phids = array(); 41 + foreach ($interfaces as $interface) { 42 + $device_phids[] = $interface->getDevicePHID(); 43 + } 44 + 45 + $device_phids = array_unique($device_phids); 46 + 47 + $devices = id(new AlmanacDeviceQuery()) 48 + ->setViewer($viewer) 49 + ->withPHIDs($device_phids) 50 + ->execute(); 51 + 52 + foreach ($devices as $device) { 53 + $device->rebuildClusterBindingStatus(); 54 + } 55 + } 56 + 57 + public function getTitle() { 58 + return pht( 59 + '%s changed the interface for this binding from %s to %s.', 60 + $this->renderAuthor(), 61 + $this->renderOldHandle(), 62 + $this->renderNewHandle()); 63 + } 64 + 65 + public function validateTransactions($object, array $xactions) { 66 + $errors = array(); 67 + 68 + $interface_phid = $object->getInterfacePHID(); 69 + if ($this->isEmptyTextTransaction($interface_phid, $xactions)) { 70 + $errors[] = $this->newRequiredError( 71 + pht('Bindings must specify an interface.')); 72 + } 73 + 74 + foreach ($xactions as $xaction) { 75 + $interface_phid = $xaction->getNewValue(); 76 + 77 + $interface = $this->loadInterface($interface_phid); 78 + if (!$interface) { 79 + $errors[] = $this->newInvalidError( 80 + pht( 81 + 'You can not bind a service to an invalid or restricted '. 82 + 'interface.'), 83 + $xaction); 84 + continue; 85 + } 86 + 87 + $binding = id(new AlmanacBindingQuery()) 88 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 89 + ->withServicePHIDs(array($object->getServicePHID())) 90 + ->withInterfacePHIDs(array($interface_phid)) 91 + ->executeOne(); 92 + if ($binding && ($binding->getID() != $object->getID())) { 93 + $errors[] = $this->newInvalidError( 94 + pht( 95 + 'You can not bind a service to the same interface multiple '. 96 + 'times.'), 97 + $xaction); 98 + continue; 99 + } 100 + } 101 + 102 + return $errors; 103 + } 104 + 105 + private function loadInterface($phid) { 106 + return id(new AlmanacInterfaceQuery()) 107 + ->setViewer($this->getActor()) 108 + ->withPHIDs(array($phid)) 109 + ->executeOne(); 110 + } 111 + }
+4
src/applications/almanac/xaction/AlmanacBindingTransactionType.php
··· 1 + <?php 2 + 3 + abstract class AlmanacBindingTransactionType 4 + extends AlmanacTransactionType {}