@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 "Repository Automation" to be configured for repositories

Summary:
Ref T182. This allows you to assign blueprints that a repository can use to perform working copy operations. Eventually, this will support "merge this" in Differential, etc.

This is just UI for now, with no material effects.

Most of this diff is just taking logic that was in the existing "Blueprints" CustomField and putting it in more general places so Diffusion (which does not use CustomFields) can also access it.

Test Plan:
- Configured repository automation for a repository.
- Removed repository automation for a repository.

Reviewers: chad

Reviewed By: chad

Subscribers: avivey

Maniphest Tasks: T182

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

+465 -156
+4
src/__phutil_library_map__.php
··· 688 688 'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php', 689 689 'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php', 690 690 'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php', 691 + 'DiffusionRepositoryEditAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php', 691 692 'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php', 692 693 'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php', 693 694 'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php', ··· 875 876 'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php', 876 877 'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php', 877 878 'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php', 879 + 'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php', 878 880 'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php', 879 881 'DrydockResource' => 'applications/drydock/storage/DrydockResource.php', 880 882 'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php', ··· 4424 4426 'DiffusionRepositoryDefaultController' => 'DiffusionController', 4425 4427 'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController', 4426 4428 'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController', 4429 + 'DiffusionRepositoryEditAutomationController' => 'DiffusionRepositoryEditController', 4427 4430 'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController', 4428 4431 'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController', 4429 4432 'DiffusionRepositoryEditController' => 'DiffusionController', ··· 4645 4648 'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow', 4646 4649 'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow', 4647 4650 'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow', 4651 + 'DrydockObjectAuthorizationView' => 'AphrontView', 4648 4652 'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4649 4653 'DrydockResource' => array( 4650 4654 'DrydockDAO',
+1
src/applications/diffusion/application/PhabricatorDiffusionApplication.php
··· 102 102 'update/' => 'DiffusionRepositoryEditUpdateController', 103 103 'symbol/' => 'DiffusionRepositorySymbolsController', 104 104 'staging/' => 'DiffusionRepositoryEditStagingController', 105 + 'automation/' => 'DiffusionRepositoryEditAutomationController', 105 106 ), 106 107 'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController', 107 108 'mirror/' => array(
+94
src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php
··· 1 + <?php 2 + 3 + final class DiffusionRepositoryEditAutomationController 4 + extends DiffusionRepositoryEditController { 5 + 6 + protected function processDiffusionRequest(AphrontRequest $request) { 7 + $viewer = $request->getUser(); 8 + $drequest = $this->diffusionRequest; 9 + $repository = $drequest->getRepository(); 10 + 11 + $repository = id(new PhabricatorRepositoryQuery()) 12 + ->setViewer($viewer) 13 + ->requireCapabilities( 14 + array( 15 + PhabricatorPolicyCapability::CAN_VIEW, 16 + PhabricatorPolicyCapability::CAN_EDIT, 17 + )) 18 + ->withIDs(array($repository->getID())) 19 + ->executeOne(); 20 + if (!$repository) { 21 + return new Aphront404Response(); 22 + } 23 + 24 + if (!$repository->supportsAutomation()) { 25 + return new Aphront404Response(); 26 + } 27 + 28 + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); 29 + 30 + $v_blueprints = $repository->getHumanReadableDetail( 31 + 'automation.blueprintPHIDs'); 32 + 33 + if ($request->isFormPost()) { 34 + $v_blueprints = $request->getArr('blueprintPHIDs'); 35 + 36 + $xactions = array(); 37 + $template = id(new PhabricatorRepositoryTransaction()); 38 + 39 + $type_blueprints = 40 + PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; 41 + 42 + $xactions[] = id(clone $template) 43 + ->setTransactionType($type_blueprints) 44 + ->setNewValue($v_blueprints); 45 + 46 + id(new PhabricatorRepositoryEditor()) 47 + ->setContinueOnNoEffect(true) 48 + ->setContentSourceFromRequest($request) 49 + ->setActor($viewer) 50 + ->applyTransactions($repository, $xactions); 51 + 52 + return id(new AphrontRedirectResponse())->setURI($edit_uri); 53 + } 54 + 55 + $crumbs = $this->buildApplicationCrumbs(); 56 + $crumbs->addTextCrumb(pht('Edit Automation')); 57 + 58 + $title = pht('Edit %s', $repository->getName()); 59 + 60 + $form = id(new AphrontFormView()) 61 + ->setUser($viewer) 62 + ->appendRemarkupInstructions( 63 + pht( 64 + "Configure **Repository Automation** to allow Phabricator to ". 65 + "write to this repository.". 66 + "\n\n". 67 + "IMPORTANT: This feature is new, experimental, and not supported. ". 68 + "Use it at your own risk.")) 69 + ->appendControl( 70 + id(new AphrontFormTokenizerControl()) 71 + ->setLabel(pht('Use Blueprints')) 72 + ->setName('blueprintPHIDs') 73 + ->setValue($v_blueprints) 74 + ->setDatasource(new DrydockBlueprintDatasource())) 75 + ->appendChild( 76 + id(new AphrontFormSubmitControl()) 77 + ->setValue(pht('Save')) 78 + ->addCancelButton($edit_uri)); 79 + 80 + $object_box = id(new PHUIObjectBoxView()) 81 + ->setHeaderText($title) 82 + ->setForm($form); 83 + 84 + return $this->buildApplicationPage( 85 + array( 86 + $crumbs, 87 + $object_box, 88 + ), 89 + array( 90 + 'title' => $title, 91 + )); 92 + } 93 + 94 + }
+55 -1
src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
··· 31 31 $has_branches = ($is_git || $is_hg); 32 32 $has_local = $repository->usesLocalWorkingCopy(); 33 33 $supports_staging = $repository->supportsStaging(); 34 + $supports_automation = $repository->supportsAutomation(); 34 35 35 36 $crumbs = $this->buildApplicationCrumbs($is_main = true); 36 37 ··· 100 101 $this->buildStagingActions($repository)); 101 102 } 102 103 104 + $automation_properties = null; 105 + if ($supports_automation) { 106 + $automation_properties = $this->buildAutomationProperties( 107 + $repository, 108 + $this->buildAutomationActions($repository)); 109 + } 110 + 103 111 $actions_properties = $this->buildActionsProperties( 104 112 $repository, 105 113 $this->buildActionsActions($repository)); ··· 169 177 $boxes[] = id(new PHUIObjectBoxView()) 170 178 ->setHeaderText(pht('Staging')) 171 179 ->addPropertyList($staging_properties); 180 + } 181 + 182 + if ($automation_properties) { 183 + $boxes[] = id(new PHUIObjectBoxView()) 184 + ->setHeaderText(pht('Automation')) 185 + ->addPropertyList($automation_properties); 172 186 } 173 187 174 188 $boxes[] = id(new PHUIObjectBoxView()) ··· 622 636 return $view; 623 637 } 624 638 625 - 626 639 private function buildStagingActions(PhabricatorRepository $repository) { 627 640 $viewer = $this->getViewer(); 628 641 ··· 657 670 $view->addProperty( 658 671 pht('Staging Area'), 659 672 $staging_uri); 673 + 674 + return $view; 675 + } 676 + 677 + private function buildAutomationActions(PhabricatorRepository $repository) { 678 + $viewer = $this->getViewer(); 679 + 680 + $view = id(new PhabricatorActionListView()) 681 + ->setObjectURI($this->getRequest()->getRequestURI()) 682 + ->setUser($viewer); 683 + 684 + $edit = id(new PhabricatorActionView()) 685 + ->setIcon('fa-pencil') 686 + ->setName(pht('Edit Automation')) 687 + ->setHref( 688 + $this->getRepositoryControllerURI($repository, 'edit/automation/')); 689 + $view->addAction($edit); 690 + 691 + return $view; 692 + } 693 + 694 + private function buildAutomationProperties( 695 + PhabricatorRepository $repository, 696 + PhabricatorActionListView $actions) { 697 + $viewer = $this->getViewer(); 698 + 699 + $view = id(new PHUIPropertyListView()) 700 + ->setUser($viewer) 701 + ->setActionList($actions); 702 + 703 + $blueprint_phids = $repository->getAutomationBlueprintPHIDs(); 704 + if (!$blueprint_phids) { 705 + $blueprint_view = phutil_tag('em', array(), pht('Not Configured')); 706 + } else { 707 + $blueprint_view = id(new DrydockObjectAuthorizationView()) 708 + ->setUser($viewer) 709 + ->setObjectPHID($repository->getPHID()) 710 + ->setBlueprintPHIDs($blueprint_phids); 711 + } 712 + 713 + $view->addProperty(pht('Automation'), $blueprint_view); 660 714 661 715 return $view; 662 716 }
+80
src/applications/drydock/storage/DrydockAuthorization.php
··· 93 93 return idx($map, $state, pht('<Unknown: %s>', $state)); 94 94 } 95 95 96 + /** 97 + * Apply external authorization effects after a user chagnes the value of a 98 + * blueprint selector control an object. 99 + * 100 + * @param PhabricatorUser User applying the change. 101 + * @param phid Object PHID change is being applied to. 102 + * @param list<phid> Old blueprint PHIDs. 103 + * @param list<phid> New blueprint PHIDs. 104 + * @return void 105 + */ 106 + public static function applyAuthorizationChanges( 107 + PhabricatorUser $viewer, 108 + $object_phid, 109 + array $old, 110 + array $new) { 111 + 112 + $old_phids = array_fuse($old); 113 + $new_phids = array_fuse($new); 114 + 115 + $rem_phids = array_diff_key($old_phids, $new_phids); 116 + $add_phids = array_diff_key($new_phids, $old_phids); 117 + 118 + $altered_phids = $rem_phids + $add_phids; 119 + 120 + if (!$altered_phids) { 121 + return; 122 + } 123 + 124 + $authorizations = id(new DrydockAuthorizationQuery()) 125 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 126 + ->withObjectPHIDs(array($object_phid)) 127 + ->withBlueprintPHIDs($altered_phids) 128 + ->execute(); 129 + $authorizations = mpull($authorizations, null, 'getBlueprintPHID'); 130 + 131 + $state_active = self::OBJECTAUTH_ACTIVE; 132 + $state_inactive = self::OBJECTAUTH_INACTIVE; 133 + 134 + $state_requested = self::BLUEPRINTAUTH_REQUESTED; 135 + 136 + // Disable the object side of the authorization for any existing 137 + // authorizations. 138 + foreach ($rem_phids as $rem_phid) { 139 + $authorization = idx($authorizations, $rem_phid); 140 + if (!$authorization) { 141 + continue; 142 + } 143 + 144 + $authorization 145 + ->setObjectAuthorizationState($state_inactive) 146 + ->save(); 147 + } 148 + 149 + // For new authorizations, either add them or reactivate them depending 150 + // on the current state. 151 + foreach ($add_phids as $add_phid) { 152 + $needs_update = false; 153 + 154 + $authorization = idx($authorizations, $add_phid); 155 + if (!$authorization) { 156 + $authorization = id(new DrydockAuthorization()) 157 + ->setObjectPHID($object_phid) 158 + ->setObjectAuthorizationState($state_active) 159 + ->setBlueprintPHID($add_phid) 160 + ->setBlueprintAuthorizationState($state_requested); 161 + 162 + $needs_update = true; 163 + } else { 164 + $current_state = $authorization->getObjectAuthorizationState(); 165 + if ($current_state != $state_active) { 166 + $authorization->setObjectAuthorizationState($state_active); 167 + $needs_update = true; 168 + } 169 + } 170 + 171 + if ($needs_update) { 172 + $authorization->save(); 173 + } 174 + } 175 + } 96 176 97 177 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 98 178
+79
src/applications/drydock/view/DrydockObjectAuthorizationView.php
··· 1 + <?php 2 + 3 + final class DrydockObjectAuthorizationView extends AphrontView { 4 + 5 + private $objectPHID; 6 + private $blueprintPHIDs; 7 + 8 + public function setObjectPHID($object_phid) { 9 + $this->objectPHID = $object_phid; 10 + return $this; 11 + } 12 + 13 + public function getObjectPHID() { 14 + return $this->objectPHID; 15 + } 16 + 17 + public function setBlueprintPHIDs(array $blueprint_phids) { 18 + $this->blueprintPHIDs = $blueprint_phids; 19 + return $this; 20 + } 21 + 22 + public function getBlueprintPHIDs() { 23 + return $this->blueprintPHIDs; 24 + } 25 + 26 + public function render() { 27 + $viewer = $this->getUser(); 28 + $blueprint_phids = $this->getBlueprintPHIDs(); 29 + $object_phid = $this->getObjectPHID(); 30 + 31 + // NOTE: We're intentionally letting you see the authorization state on 32 + // blueprints you can't see because this has a tremendous potential to 33 + // be extremely confusing otherwise. You still can't see the blueprints 34 + // themselves, but you can know if the object is authorized on something. 35 + 36 + if ($blueprint_phids) { 37 + $handles = $viewer->loadHandles($blueprint_phids); 38 + 39 + $authorizations = id(new DrydockAuthorizationQuery()) 40 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 41 + ->withObjectPHIDs(array($object_phid)) 42 + ->withBlueprintPHIDs($blueprint_phids) 43 + ->execute(); 44 + $authorizations = mpull($authorizations, null, 'getBlueprintPHID'); 45 + } else { 46 + $handles = array(); 47 + $authorizations = array(); 48 + } 49 + 50 + $items = array(); 51 + foreach ($blueprint_phids as $phid) { 52 + $authorization = idx($authorizations, $phid); 53 + if (!$authorization) { 54 + continue; 55 + } 56 + 57 + $handle = $handles[$phid]; 58 + 59 + $item = id(new PHUIStatusItemView()) 60 + ->setTarget($handle->renderLink()); 61 + 62 + $state = $authorization->getBlueprintAuthorizationState(); 63 + $item->setIcon( 64 + DrydockAuthorization::getBlueprintStateIcon($state), 65 + null, 66 + DrydockAuthorization::getBlueprintStateName($state)); 67 + 68 + $items[] = $item; 69 + } 70 + 71 + $status = new PHUIStatusListView(); 72 + foreach ($items as $item) { 73 + $status->addItem($item); 74 + } 75 + 76 + return $status; 77 + } 78 + 79 + }
+36
src/applications/phid/query/PhabricatorObjectQuery.php
··· 178 178 return null; 179 179 } 180 180 181 + 182 + /** 183 + * Select invalid or restricted PHIDs from a list. 184 + * 185 + * PHIDs are invalid if their objects do not exist or can not be seen by the 186 + * viewer. This method is generally used to validate that PHIDs affected by 187 + * a transaction are valid. 188 + * 189 + * @param PhabricatorUser Viewer. 190 + * @param list<phid> List of ostensibly valid PHIDs. 191 + * @return list<phid> List of invalid or restricted PHIDs. 192 + */ 193 + public static function loadInvalidPHIDsForViewer( 194 + PhabricatorUser $viewer, 195 + array $phids) { 196 + 197 + if (!$phids) { 198 + return array(); 199 + } 200 + 201 + $objects = id(new PhabricatorObjectQuery()) 202 + ->setViewer($viewer) 203 + ->withPHIDs($phids) 204 + ->execute(); 205 + $objects = mpull($objects, null, 'getPHID'); 206 + 207 + $invalid = array(); 208 + foreach ($phids as $phid) { 209 + if (empty($objects[$phid])) { 210 + $invalid[] = $phid; 211 + } 212 + } 213 + 214 + return $invalid; 215 + } 216 + 181 217 }
+40 -23
src/applications/repository/editor/PhabricatorRepositoryEditor.php
··· 44 44 $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE; 45 45 $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; 46 46 $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; 47 + $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; 47 48 48 49 $types[] = PhabricatorTransactions::TYPE_EDGE; 49 50 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; ··· 107 108 return $object->getSymbolSources(); 108 109 case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: 109 110 return $object->getDetail('staging-uri'); 111 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 112 + return $object->getDetail('automation.blueprintPHIDs', array()); 110 113 } 111 114 } 112 115 ··· 143 146 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: 144 147 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: 145 148 case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: 149 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 146 150 return $xaction->getNewValue(); 147 151 case PhabricatorRepositoryTransaction::TYPE_NOTIFY: 148 152 case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: ··· 226 230 case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: 227 231 $object->setDetail('staging-uri', $xaction->getNewValue()); 228 232 return; 233 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 234 + $object->setDetail( 235 + 'automation.blueprintPHIDs', 236 + $xaction->getNewValue()); 237 + return; 229 238 case PhabricatorRepositoryTransaction::TYPE_ENCODING: 230 239 // Make sure the encoding is valid by converting to UTF-8. This tests 231 240 // that the user has mbstring installed, and also that they didn't type ··· 275 284 } 276 285 277 286 $editor->save(); 287 + break; 288 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 289 + DrydockAuthorization::applyAuthorizationChanges( 290 + $this->getActor(), 291 + $object->getPHID(), 292 + $xaction->getOldValue(), 293 + $xaction->getNewValue()); 278 294 break; 279 295 } 280 296 281 297 } 282 298 283 - protected function mergeTransactions( 284 - PhabricatorApplicationTransaction $u, 285 - PhabricatorApplicationTransaction $v) { 286 - 287 - $type = $u->getTransactionType(); 288 - switch ($type) {} 289 - 290 - return parent::mergeTransactions($u, $v); 291 - } 292 - 293 - protected function transactionHasEffect( 294 - PhabricatorLiskDAO $object, 295 - PhabricatorApplicationTransaction $xaction) { 296 - 297 - $old = $xaction->getOldValue(); 298 - $new = $xaction->getNewValue(); 299 - 300 - $type = $xaction->getTransactionType(); 301 - switch ($type) {} 302 - 303 - return parent::transactionHasEffect($object, $xaction); 304 - } 305 - 306 299 protected function requireCapabilities( 307 300 PhabricatorLiskDAO $object, 308 301 PhabricatorApplicationTransaction $xaction) { ··· 338 331 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: 339 332 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: 340 333 case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: 334 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 341 335 PhabricatorPolicyFilter::requireCapability( 342 336 $this->requireActor(), 343 337 $object, ··· 427 421 pht( 428 422 'The selected credential does not exist, or you do not have '. 429 423 'permission to use it.'), 424 + $xaction); 425 + } 426 + } 427 + break; 428 + 429 + case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: 430 + foreach ($xactions as $xaction) { 431 + $old = nonempty($xaction->getOldValue(), array()); 432 + $new = nonempty($xaction->getNewValue(), array()); 433 + 434 + $add = array_diff($new, $old); 435 + 436 + $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer( 437 + $this->getActor(), 438 + $add); 439 + if ($invalid) { 440 + $errors[] = new PhabricatorApplicationTransactionValidationError( 441 + $type, 442 + pht('Invalid'), 443 + pht( 444 + 'Some of the selected automation blueprints are invalid '. 445 + 'or restricted: %s.', 446 + implode(', ', $invalid)), 430 447 $xaction); 431 448 } 432 449 }
+17 -1
src/applications/repository/storage/PhabricatorRepository.php
··· 1799 1799 } 1800 1800 1801 1801 1802 - /* -( Staging )-------------------------------------------------------------*/ 1802 + /* -( Staging )------------------------------------------------------------ */ 1803 1803 1804 1804 1805 1805 public function supportsStaging() { ··· 1812 1812 return null; 1813 1813 } 1814 1814 return $this->getDetail('staging-uri', null); 1815 + } 1816 + 1817 + 1818 + /* -( Automation )--------------------------------------------------------- */ 1819 + 1820 + 1821 + public function supportsAutomation() { 1822 + return $this->isGit(); 1823 + } 1824 + 1825 + 1826 + public function getAutomationBlueprintPHIDs() { 1827 + if (!$this->supportsAutomation()) { 1828 + return array(); 1829 + } 1830 + return $this->getDetail('automation.blueprintPHIDs', array()); 1815 1831 } 1816 1832 1817 1833
+30
src/applications/repository/storage/PhabricatorRepositoryTransaction.php
··· 28 28 const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source'; 29 29 const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language'; 30 30 const TYPE_STAGING_URI = 'repo:staging-uri'; 31 + const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints'; 31 32 32 33 // TODO: Clean up these legacy transaction types. 33 34 const TYPE_SSH_LOGIN = 'repo:ssh-login'; ··· 65 66 } 66 67 break; 67 68 case self::TYPE_SYMBOLS_SOURCES: 69 + case self::TYPE_AUTOMATION_BLUEPRINTS: 68 70 if ($old) { 69 71 $phids = array_merge($phids, $old); 70 72 } ··· 435 437 $this->renderHandleLink($author_phid), 436 438 $old, 437 439 $new); 440 + } 441 + 442 + case self::TYPE_AUTOMATION_BLUEPRINTS: 443 + $add = array_diff($new, $old); 444 + $rem = array_diff($old, $new); 445 + 446 + if ($add && $rem) { 447 + return pht( 448 + '%s changed %s automation blueprint(s), '. 449 + 'added %s: %s; removed %s: %s.', 450 + $this->renderHandleLink($author_phid), 451 + new PhutilNumber(count($add) + count($rem)), 452 + new PhutilNumber(count($add)), 453 + $this->renderHandleList($add), 454 + new PhutilNumber(count($rem)), 455 + $this->renderHandleList($rem)); 456 + } else if ($add) { 457 + return pht( 458 + '%s added %s automation blueprint(s): %s.', 459 + $this->renderHandleLink($author_phid), 460 + new PhutilNumber(count($add)), 461 + $this->renderHandleList($add)); 462 + } else { 463 + return pht( 464 + '%s removed %s automation blueprint(s): %s.', 465 + $this->renderHandleLink($author_phid), 466 + new PhutilNumber(count($rem)), 467 + $this->renderHandleList($rem)); 438 468 } 439 469 } 440 470
+9 -115
src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
··· 14 14 public function applyApplicationTransactionExternalEffects( 15 15 PhabricatorApplicationTransaction $xaction) { 16 16 17 - $object_phid = $xaction->getObjectPHID(); 18 - 19 17 $old = $this->decodeValue($xaction->getOldValue()); 20 18 $new = $this->decodeValue($xaction->getNewValue()); 21 19 22 - $old_phids = array_fuse($old); 23 - $new_phids = array_fuse($new); 24 - 25 - $rem_phids = array_diff_key($old_phids, $new_phids); 26 - $add_phids = array_diff_key($new_phids, $old_phids); 27 - 28 - $altered_phids = $rem_phids + $add_phids; 29 - 30 - if (!$altered_phids) { 31 - return; 32 - } 33 - 34 - $authorizations = id(new DrydockAuthorizationQuery()) 35 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 36 - ->withObjectPHIDs(array($object_phid)) 37 - ->withBlueprintPHIDs($altered_phids) 38 - ->execute(); 39 - $authorizations = mpull($authorizations, null, 'getBlueprintPHID'); 40 - 41 - $state_active = DrydockAuthorization::OBJECTAUTH_ACTIVE; 42 - $state_inactive = DrydockAuthorization::OBJECTAUTH_INACTIVE; 43 - 44 - $state_requested = DrydockAuthorization::BLUEPRINTAUTH_REQUESTED; 45 - 46 - // Disable the object side of the authorization for any existing 47 - // authorizations. 48 - foreach ($rem_phids as $rem_phid) { 49 - $authorization = idx($authorizations, $rem_phid); 50 - if (!$authorization) { 51 - continue; 52 - } 53 - 54 - $authorization 55 - ->setObjectAuthorizationState($state_inactive) 56 - ->save(); 57 - } 58 - 59 - // For new authorizations, either add them or reactivate them depending 60 - // on the current state. 61 - foreach ($add_phids as $add_phid) { 62 - $needs_update = false; 63 - 64 - $authorization = idx($authorizations, $add_phid); 65 - if (!$authorization) { 66 - $authorization = id(new DrydockAuthorization()) 67 - ->setObjectPHID($object_phid) 68 - ->setObjectAuthorizationState($state_active) 69 - ->setBlueprintPHID($add_phid) 70 - ->setBlueprintAuthorizationState($state_requested); 71 - 72 - $needs_update = true; 73 - } else { 74 - $current_state = $authorization->getObjectAuthorizationState(); 75 - if ($current_state != $state_active) { 76 - $authorization->setObjectAuthorizationState($state_active); 77 - $needs_update = true; 78 - } 79 - } 80 - 81 - if ($needs_update) { 82 - $authorization->save(); 83 - } 84 - } 85 - 20 + DrydockAuthorization::applyAuthorizationChanges( 21 + $this->getViewer(), 22 + $xaction->getObjectPHID(), 23 + $old, 24 + $new); 86 25 } 87 26 88 27 public function renderPropertyViewValue(array $handles) { ··· 91 30 return phutil_tag('em', array(), pht('No authorized blueprints.')); 92 31 } 93 32 94 - $object = $this->getObject(); 95 - $object_phid = $object->getPHID(); 96 - 97 - // NOTE: We're intentionally letting you see the authorization state on 98 - // blueprints you can't see because this has a tremendous potential to 99 - // be extremely confusing otherwise. You still can't see the blueprints 100 - // themselves, but you can know if the object is authorized on something. 101 - 102 - if ($value) { 103 - $handles = $this->getViewer()->loadHandles($value); 104 - 105 - $authorizations = id(new DrydockAuthorizationQuery()) 106 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 107 - ->withObjectPHIDs(array($object_phid)) 108 - ->withBlueprintPHIDs($value) 109 - ->execute(); 110 - $authorizations = mpull($authorizations, null, 'getBlueprintPHID'); 111 - } else { 112 - $handles = array(); 113 - $authorizations = array(); 114 - } 115 - 116 - $items = array(); 117 - foreach ($value as $phid) { 118 - $authorization = idx($authorizations, $phid); 119 - if (!$authorization) { 120 - continue; 121 - } 122 - 123 - $handle = $handles[$phid]; 124 - 125 - $item = id(new PHUIStatusItemView()) 126 - ->setTarget($handle->renderLink()); 127 - 128 - $state = $authorization->getBlueprintAuthorizationState(); 129 - $item->setIcon( 130 - DrydockAuthorization::getBlueprintStateIcon($state), 131 - null, 132 - DrydockAuthorization::getBlueprintStateName($state)); 133 - 134 - $items[] = $item; 135 - } 136 - 137 - $status = new PHUIStatusListView(); 138 - foreach ($items as $item) { 139 - $status->addItem($item); 140 - } 141 - 142 - return $status; 33 + return id(new DrydockObjectAuthorizationView()) 34 + ->setUser($this->getViewer()) 35 + ->setObjectPHID($this->getObject()->getPHID()) 36 + ->setBlueprintPHIDs($value); 143 37 } 144 38 145 39
+3 -16
src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
··· 158 158 159 159 $add = array_diff($new, $old); 160 160 161 - if (!$add) { 162 - continue; 163 - } 164 - 165 - $objects = id(new PhabricatorObjectQuery()) 166 - ->setViewer($editor->getActor()) 167 - ->withPHIDs($add) 168 - ->execute(); 169 - $objects = mpull($objects, null, 'getPHID'); 170 - 171 - $invalid = array(); 172 - foreach ($add as $phid) { 173 - if (empty($objects[$phid])) { 174 - $invalid[] = $phid; 175 - } 176 - } 161 + $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer( 162 + $editor->getActor(), 163 + $add); 177 164 178 165 if ($invalid) { 179 166 $error = new PhabricatorApplicationTransactionValidationError(
+17
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 1403 1403 'Waiting %s seconds for lease to activate.', 1404 1404 ), 1405 1405 1406 + '%s changed %s automation blueprint(s), added %s: %s; removed %s: %s.' => 1407 + '%s changed automation blueprints, added: %4$s; removed: %6$s.', 1408 + 1409 + '%s added %s automation blueprint(s): %s.' => array( 1410 + array( 1411 + '%s added an automation blueprint: %3$s.', 1412 + '%s added automation blueprints: %3$s.', 1413 + ), 1414 + ), 1415 + 1416 + '%s removed %s automation blueprint(s): %s.' => array( 1417 + array( 1418 + '%s removed an automation blueprint: %3$s.', 1419 + '%s removed automation blueprints: %3$s.', 1420 + ), 1421 + ), 1422 + 1406 1423 ); 1407 1424 } 1408 1425