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

Migrate Project parent and milestone to modular transactions

Test Plan: Unit tests pass. Went through the UI for creating new subprojects and milestones, but didn't setup some API calls to check that all the validation errors were still caught.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: Korvin, epriestley

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

+152 -118
+6
src/__phutil_library_map__.php
··· 3660 3660 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', 3661 3661 'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php', 3662 3662 'PhabricatorProjectMenuItemController' => 'applications/project/controller/PhabricatorProjectMenuItemController.php', 3663 + 'PhabricatorProjectMilestoneTransaction' => 'applications/project/xaction/PhabricatorProjectMilestoneTransaction.php', 3663 3664 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', 3664 3665 'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php', 3665 3666 'PhabricatorProjectNameTransaction' => 'applications/project/xaction/PhabricatorProjectNameTransaction.php', ··· 3668 3669 'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php', 3669 3670 'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php', 3670 3671 'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php', 3672 + 'PhabricatorProjectParentTransaction' => 'applications/project/xaction/PhabricatorProjectParentTransaction.php', 3671 3673 'PhabricatorProjectPictureProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectPictureProfileMenuItem.php', 3672 3674 'PhabricatorProjectPointsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php', 3673 3675 'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php', ··· 3696 3698 'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php', 3697 3699 'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php', 3698 3700 'PhabricatorProjectTransactionType' => 'applications/project/xaction/PhabricatorProjectTransactionType.php', 3701 + 'PhabricatorProjectTypeTransaction' => 'applications/project/xaction/PhabricatorProjectTypeTransaction.php', 3699 3702 'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php', 3700 3703 'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php', 3701 3704 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', ··· 9093 9096 'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController', 9094 9097 'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController', 9095 9098 'PhabricatorProjectMenuItemController' => 'PhabricatorProjectController', 9099 + 'PhabricatorProjectMilestoneTransaction' => 'PhabricatorProjectTypeTransaction', 9096 9100 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', 9097 9101 'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar', 9098 9102 'PhabricatorProjectNameTransaction' => 'PhabricatorProjectTransactionType', ··· 9101 9105 'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9102 9106 'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 9103 9107 'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver', 9108 + 'PhabricatorProjectParentTransaction' => 'PhabricatorProjectTypeTransaction', 9104 9109 'PhabricatorProjectPictureProfileMenuItem' => 'PhabricatorProfileMenuItem', 9105 9110 'PhabricatorProjectPointsProfileMenuItem' => 'PhabricatorProfileMenuItem', 9106 9111 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', ··· 9132 9137 'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 9133 9138 'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 9134 9139 'PhabricatorProjectTransactionType' => 'PhabricatorModularTransactionType', 9140 + 'PhabricatorProjectTypeTransaction' => 'PhabricatorProjectTransactionType', 9135 9141 'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener', 9136 9142 'PhabricatorProjectUpdateController' => 'PhabricatorProjectController', 9137 9143 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
+4 -2
src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
··· 1447 1447 if ($parent) { 1448 1448 if ($is_milestone) { 1449 1449 $xactions[] = id(new PhabricatorProjectTransaction()) 1450 - ->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE) 1450 + ->setTransactionType( 1451 + PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE) 1451 1452 ->setNewValue($parent->getPHID()); 1452 1453 } else { 1453 1454 $xactions[] = id(new PhabricatorProjectTransaction()) 1454 - ->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT) 1455 + ->setTransactionType( 1456 + PhabricatorProjectParentTransaction::TRANSACTIONTYPE) 1455 1457 ->setNewValue($parent->getPHID()); 1456 1458 } 1457 1459 }
+5 -111
src/applications/project/editor/PhabricatorProjectTransactionEditor.php
··· 30 30 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 31 31 $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; 32 32 33 - $types[] = PhabricatorProjectTransaction::TYPE_PARENT; 34 - $types[] = PhabricatorProjectTransaction::TYPE_MILESTONE; 35 33 $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; 36 34 $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; 37 35 $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; ··· 47 45 switch ($xaction->getTransactionType()) { 48 46 case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 49 47 return (int)$object->getHasWorkboard(); 50 - case PhabricatorProjectTransaction::TYPE_PARENT: 51 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 52 - return null; 53 48 case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: 54 49 return $object->getDefaultWorkboardSort(); 55 50 case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: ··· 66 61 PhabricatorApplicationTransaction $xaction) { 67 62 68 63 switch ($xaction->getTransactionType()) { 69 - case PhabricatorProjectTransaction::TYPE_PARENT: 70 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 71 64 case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: 72 65 case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: 73 66 return $xaction->getNewValue(); ··· 89 82 PhabricatorApplicationTransaction $xaction) { 90 83 91 84 switch ($xaction->getTransactionType()) { 92 - case PhabricatorProjectTransaction::TYPE_PARENT: 93 - $object->setParentProjectPHID($xaction->getNewValue()); 94 - return; 95 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 96 - $number = $object->getParentProject()->loadNextMilestoneNumber(); 97 - $object->setMilestoneNumber($number); 98 - $object->setParentProjectPHID($xaction->getNewValue()); 99 - return; 100 85 case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 101 86 $object->setHasWorkboard($xaction->getNewValue()); 102 87 return; ··· 122 107 $new = $xaction->getNewValue(); 123 108 124 109 switch ($xaction->getTransactionType()) { 125 - case PhabricatorProjectTransaction::TYPE_PARENT: 126 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 127 110 case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: 128 111 case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: 129 112 case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: ··· 145 128 $parent_xaction = null; 146 129 foreach ($xactions as $xaction) { 147 130 switch ($xaction->getTransactionType()) { 148 - case PhabricatorProjectTransaction::TYPE_PARENT: 149 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 131 + case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: 132 + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: 150 133 if ($xaction->getNewValue() === null) { 151 134 continue; 152 135 } ··· 208 191 return $errors; 209 192 } 210 193 211 - protected function validateTransaction( 212 - PhabricatorLiskDAO $object, 213 - $type, 214 - array $xactions) { 215 - 216 - $errors = parent::validateTransaction($object, $type, $xactions); 217 - 218 - switch ($type) { 219 - case PhabricatorProjectTransaction::TYPE_PARENT: 220 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 221 - if (!$xactions) { 222 - break; 223 - } 224 - 225 - $xaction = last($xactions); 226 - 227 - $parent_phid = $xaction->getNewValue(); 228 - if (!$parent_phid) { 229 - continue; 230 - } 231 - 232 - if (!$this->getIsNewObject()) { 233 - $errors[] = new PhabricatorApplicationTransactionValidationError( 234 - $type, 235 - pht('Invalid'), 236 - pht( 237 - 'You can only set a parent or milestone project when creating a '. 238 - 'project for the first time.'), 239 - $xaction); 240 - break; 241 - } 242 - 243 - $projects = id(new PhabricatorProjectQuery()) 244 - ->setViewer($this->requireActor()) 245 - ->withPHIDs(array($parent_phid)) 246 - ->requireCapabilities( 247 - array( 248 - PhabricatorPolicyCapability::CAN_VIEW, 249 - PhabricatorPolicyCapability::CAN_EDIT, 250 - )) 251 - ->execute(); 252 - if (!$projects) { 253 - $errors[] = new PhabricatorApplicationTransactionValidationError( 254 - $type, 255 - pht('Invalid'), 256 - pht( 257 - 'Parent or milestone project PHID ("%s") must be the PHID of a '. 258 - 'valid, visible project which you have permission to edit.', 259 - $parent_phid), 260 - $xaction); 261 - break; 262 - } 263 - 264 - $project = head($projects); 265 - 266 - if ($project->isMilestone()) { 267 - $errors[] = new PhabricatorApplicationTransactionValidationError( 268 - $type, 269 - pht('Invalid'), 270 - pht( 271 - 'Parent or milestone project PHID ("%s") must not be a '. 272 - 'milestone. Milestones may not have subprojects or milestones.', 273 - $parent_phid), 274 - $xaction); 275 - break; 276 - } 277 - 278 - $limit = PhabricatorProject::getProjectDepthLimit(); 279 - if ($project->getProjectDepth() >= ($limit - 1)) { 280 - $errors[] = new PhabricatorApplicationTransactionValidationError( 281 - $type, 282 - pht('Invalid'), 283 - pht( 284 - 'You can not create a subproject or mielstone under this parent '. 285 - 'because it would nest projects too deeply. The maximum '. 286 - 'nesting depth of projects is %s.', 287 - new PhutilNumber($limit)), 288 - $xaction); 289 - break; 290 - } 291 - 292 - $object->attachParentProject($project); 293 - break; 294 - } 295 - 296 - return $errors; 297 - } 298 - 299 - 300 194 protected function requireCapabilities( 301 195 PhabricatorLiskDAO $object, 302 196 PhabricatorApplicationTransaction $xaction) { ··· 458 352 break; 459 353 } 460 354 break; 461 - case PhabricatorProjectTransaction::TYPE_PARENT: 462 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 355 + case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: 356 + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: 463 357 $materialize = true; 464 358 $new_parent = $object->getParentProject(); 465 359 break; ··· 634 528 $is_milestone = $object->isMilestone(); 635 529 foreach ($xactions as $xaction) { 636 530 switch ($xaction->getTransactionType()) { 637 - case PhabricatorProjectTransaction::TYPE_MILESTONE: 531 + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: 638 532 if ($xaction->getNewValue() !== null) { 639 533 $is_milestone = true; 640 534 }
+6 -3
src/applications/project/engine/PhabricatorProjectEditEngine.php
··· 202 202 pht('Choose a parent project to create a subproject beneath.')) 203 203 ->setConduitTypeDescription(pht('PHID of the parent project.')) 204 204 ->setAliases(array('parentPHID')) 205 - ->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT) 205 + ->setTransactionType( 206 + PhabricatorProjectParentTransaction::TRANSACTIONTYPE) 206 207 ->setHandleParameterType(new AphrontPHIDHTTPParameterType()) 207 208 ->setSingleValue($parent_phid) 208 209 ->setIsReorderable(false) ··· 217 218 pht('Choose a parent project to create a new milestone for.')) 218 219 ->setConduitTypeDescription(pht('PHID of the parent project.')) 219 220 ->setAliases(array('milestonePHID')) 220 - ->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE) 221 + ->setTransactionType( 222 + PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE) 221 223 ->setHandleParameterType(new AphrontPHIDHTTPParameterType()) 222 224 ->setSingleValue($milestone_phid) 223 225 ->setIsReorderable(false) ··· 244 246 id(new PhabricatorIconSetEditField()) 245 247 ->setKey('icon') 246 248 ->setLabel(pht('Icon')) 247 - ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) 249 + ->setTransactionType( 250 + PhabricatorProjectIconTransaction::TRANSACTIONTYPE) 248 251 ->setIconSet(new PhabricatorProjectIconSet()) 249 252 ->setDescription(pht('Project icon.')) 250 253 ->setConduitDescription(pht('Change the project icon.'))
-2
src/applications/project/storage/PhabricatorProjectTransaction.php
··· 3 3 final class PhabricatorProjectTransaction 4 4 extends PhabricatorModularTransaction { 5 5 6 - const TYPE_PARENT = 'project:parent'; 7 - const TYPE_MILESTONE = 'project:milestone'; 8 6 const TYPE_HASWORKBOARD = 'project:hasworkboard'; 9 7 const TYPE_DEFAULT_SORT = 'project:sort'; 10 8 const TYPE_DEFAULT_FILTER = 'project:filter';
+31
src/applications/project/xaction/PhabricatorProjectMilestoneTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectMilestoneTransaction 4 + extends PhabricatorProjectTypeTransaction { 5 + 6 + const TRANSACTIONTYPE = 'project:milestone'; 7 + 8 + public function generateOldValue($object) { 9 + return null; 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $parent_phid = $value; 14 + $project = id(new PhabricatorProjectQuery()) 15 + ->setViewer($this->getActor()) 16 + ->withPHIDs(array($parent_phid)) 17 + ->requireCapabilities( 18 + array( 19 + PhabricatorPolicyCapability::CAN_VIEW, 20 + PhabricatorPolicyCapability::CAN_EDIT, 21 + )) 22 + ->executeOne(); 23 + 24 + $object->attachParentProject($project); 25 + 26 + $number = $object->getParentProject()->loadNextMilestoneNumber(); 27 + $object->setMilestoneNumber($number); 28 + $object->setParentProjectPHID($value); 29 + } 30 + 31 + }
+29
src/applications/project/xaction/PhabricatorProjectParentTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectParentTransaction 4 + extends PhabricatorProjectTypeTransaction { 5 + 6 + const TRANSACTIONTYPE = 'project:parent'; 7 + 8 + public function generateOldValue($object) { 9 + return null; 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $parent_phid = $value; 14 + $project = id(new PhabricatorProjectQuery()) 15 + ->setViewer($this->getActor()) 16 + ->withPHIDs(array($parent_phid)) 17 + ->requireCapabilities( 18 + array( 19 + PhabricatorPolicyCapability::CAN_VIEW, 20 + PhabricatorPolicyCapability::CAN_EDIT, 21 + )) 22 + ->executeOne(); 23 + 24 + $object->attachParentProject($project); 25 + 26 + $object->setParentProjectPHID($value); 27 + } 28 + 29 + }
+71
src/applications/project/xaction/PhabricatorProjectTypeTransaction.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorProjectTypeTransaction 4 + extends PhabricatorProjectTransactionType { 5 + 6 + public function validateTransactions($object, array $xactions) { 7 + $errors = array(); 8 + 9 + if (!$xactions) { 10 + return $errors; 11 + } 12 + 13 + $xaction = last($xactions); 14 + 15 + $parent_phid = $xaction->getNewValue(); 16 + if (!$parent_phid) { 17 + return $errors; 18 + } 19 + 20 + if (!$this->getEditor()->getIsNewObject()) { 21 + $errors[] = $this->newInvalidError( 22 + pht( 23 + 'You can only set a parent or milestone project when creating a '. 24 + 'project for the first time.')); 25 + return $errors; 26 + } 27 + 28 + $projects = id(new PhabricatorProjectQuery()) 29 + ->setViewer($this->getActor()) 30 + ->withPHIDs(array($parent_phid)) 31 + ->requireCapabilities( 32 + array( 33 + PhabricatorPolicyCapability::CAN_VIEW, 34 + PhabricatorPolicyCapability::CAN_EDIT, 35 + )) 36 + ->execute(); 37 + if (!$projects) { 38 + $errors[] = $this->newInvalidError( 39 + pht( 40 + 'Parent or milestone project PHID ("%s") must be the PHID of a '. 41 + 'valid, visible project which you have permission to edit.', 42 + $parent_phid)); 43 + return $errors; 44 + } 45 + 46 + $project = head($projects); 47 + 48 + if ($project->isMilestone()) { 49 + $errors[] = $this->newInvalidError( 50 + pht( 51 + 'Parent or milestone project PHID ("%s") must not be a '. 52 + 'milestone. Milestones may not have subprojects or milestones.', 53 + $parent_phid)); 54 + return $errors; 55 + } 56 + 57 + $limit = PhabricatorProject::getProjectDepthLimit(); 58 + if ($project->getProjectDepth() >= ($limit - 1)) { 59 + $errors[] = $this->newInvalidError( 60 + pht( 61 + 'You can not create a subproject or milestone under this parent '. 62 + 'because it would nest projects too deeply. The maximum '. 63 + 'nesting depth of projects is %s.', 64 + new PhutilNumber($limit))); 65 + return $errors; 66 + } 67 + 68 + return $errors; 69 + } 70 + 71 + }