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

Restore "Reclaim" and "Abandon" actions to Differential on EditEngine

Summary: Ref T11114. This begins restoring comment actions to Differential, but on top of EditEngine.

Test Plan: {F2263148}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11114

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

+343 -10
+9 -9
resources/celerity/map.php
··· 9 9 'names' => array( 10 10 'conpherence.pkg.css' => '0b64e988', 11 11 'conpherence.pkg.js' => '6249a1cf', 12 - 'core.pkg.css' => '404132bb', 12 + 'core.pkg.css' => '202700e2', 13 13 'core.pkg.js' => '28e8cda8', 14 14 'darkconsole.pkg.js' => 'e7393ebb', 15 15 'differential.pkg.css' => 'a4ba74b5', ··· 146 146 'rsrc/css/phui/phui-document.css' => 'c32e8dec', 147 147 'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9', 148 148 'rsrc/css/phui/phui-fontkit.css' => '9cda225e', 149 - 'rsrc/css/phui/phui-form-view.css' => 'cd79ff6a', 149 + 'rsrc/css/phui/phui-form-view.css' => '04cc4771', 150 150 'rsrc/css/phui/phui-form.css' => '2342b0e5', 151 151 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', 152 152 'rsrc/css/phui/phui-header-view.css' => '6ec8f155', ··· 542 542 'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262', 543 543 'rsrc/js/phuix/PHUIXAutocomplete.js' => '6d86ce8b', 544 544 'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da', 545 - 'rsrc/js/phuix/PHUIXFormControl.js' => '301b7812', 545 + 'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d', 546 546 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 547 547 ), 548 548 'symbols' => array( ··· 860 860 'phui-font-icon-base-css' => '870a7360', 861 861 'phui-fontkit-css' => '9cda225e', 862 862 'phui-form-css' => '2342b0e5', 863 - 'phui-form-view-css' => 'cd79ff6a', 863 + 'phui-form-view-css' => '04cc4771', 864 864 'phui-head-thing-view-css' => 'fd311e5f', 865 865 'phui-header-view-css' => '6ec8f155', 866 866 'phui-hovercard' => '1bd28176', ··· 901 901 'phuix-action-view' => '8cf6d262', 902 902 'phuix-autocomplete' => '6d86ce8b', 903 903 'phuix-dropdown-menu' => '82e270da', 904 - 'phuix-form-control-view' => '301b7812', 904 + 'phuix-form-control-view' => 'bbece68d', 905 905 'phuix-icon-view' => 'bff6884b', 906 906 'policy-css' => '957ea14c', 907 907 'policy-edit-css' => '815c66f7', ··· 1158 1158 ), 1159 1159 '2ee659ce' => array( 1160 1160 'javelin-install', 1161 - ), 1162 - '301b7812' => array( 1163 - 'javelin-install', 1164 - 'javelin-dom', 1165 1161 ), 1166 1162 '320810c8' => array( 1167 1163 'javelin-install', ··· 1915 1911 'javelin-dom', 1916 1912 'javelin-vector', 1917 1913 'javelin-install', 1914 + ), 1915 + 'bbece68d' => array( 1916 + 'javelin-install', 1917 + 'javelin-dom', 1918 1918 ), 1919 1919 'bcaccd64' => array( 1920 1920 'javelin-behavior',
+10
src/__phutil_library_map__.php
··· 505 505 'DifferentialReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersHeraldAction.php', 506 506 'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php', 507 507 'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php', 508 + 'DifferentialRevisionAbandonTransaction' => 'applications/differential/xaction/DifferentialRevisionAbandonTransaction.php', 509 + 'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php', 508 510 'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php', 509 511 'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php', 510 512 'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php', ··· 539 541 'DifferentialRevisionPackageHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageHeraldField.php', 540 542 'DifferentialRevisionPackageOwnerHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageOwnerHeraldField.php', 541 543 'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php', 544 + 'DifferentialRevisionReclaimTransaction' => 'applications/differential/xaction/DifferentialRevisionReclaimTransaction.php', 542 545 'DifferentialRevisionRelationship' => 'applications/differential/relationships/DifferentialRevisionRelationship.php', 543 546 'DifferentialRevisionRelationshipSource' => 'applications/search/relationship/DifferentialRevisionRelationshipSource.php', 544 547 'DifferentialRevisionRepositoryHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryHeraldField.php', ··· 1840 1843 'PhabricatorApplicationsApplication' => 'applications/meta/application/PhabricatorApplicationsApplication.php', 1841 1844 'PhabricatorApplicationsController' => 'applications/meta/controller/PhabricatorApplicationsController.php', 1842 1845 'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php', 1846 + 'PhabricatorApplyEditField' => 'applications/transactions/editfield/PhabricatorApplyEditField.php', 1843 1847 'PhabricatorAsanaAuthProvider' => 'applications/auth/provider/PhabricatorAsanaAuthProvider.php', 1844 1848 'PhabricatorAsanaConfigOptions' => 'applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php', 1845 1849 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php', ··· 2572 2576 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 2573 2577 'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php', 2574 2578 'PhabricatorEditEngineSettingsPanel' => 'applications/settings/panel/PhabricatorEditEngineSettingsPanel.php', 2579 + 'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php', 2575 2580 'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php', 2576 2581 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 2577 2582 'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php', ··· 5169 5174 'PhabricatorFulltextInterface', 5170 5175 'PhabricatorConduitResultInterface', 5171 5176 ), 5177 + 'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction', 5178 + 'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType', 5172 5179 'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField', 5173 5180 'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField', 5174 5181 'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField', ··· 5203 5210 'DifferentialRevisionPackageHeraldField' => 'DifferentialRevisionHeraldField', 5204 5211 'DifferentialRevisionPackageOwnerHeraldField' => 'DifferentialRevisionHeraldField', 5205 5212 'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5213 + 'DifferentialRevisionReclaimTransaction' => 'DifferentialRevisionActionTransaction', 5206 5214 'DifferentialRevisionRelationship' => 'PhabricatorObjectRelationship', 5207 5215 'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource', 5208 5216 'DifferentialRevisionRepositoryHeraldField' => 'DifferentialRevisionHeraldField', ··· 6688 6696 'PhabricatorApplicationsApplication' => 'PhabricatorApplication', 6689 6697 'PhabricatorApplicationsController' => 'PhabricatorController', 6690 6698 'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController', 6699 + 'PhabricatorApplyEditField' => 'PhabricatorEditField', 6691 6700 'PhabricatorAsanaAuthProvider' => 'PhabricatorOAuth2AuthProvider', 6692 6701 'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions', 6693 6702 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType', ··· 7545 7554 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 7546 7555 'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction', 7547 7556 'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel', 7557 + 'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction', 7548 7558 'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction', 7549 7559 'PhabricatorEditField' => 'Phobject', 7550 7560 'PhabricatorEditPage' => 'Phobject',
+7 -1
src/applications/differential/editor/DifferentialRevisionEditEngine.php
··· 87 87 } 88 88 89 89 protected function buildCustomEditFields($object) { 90 + $viewer = $this->getViewer(); 90 91 91 92 $plan_required = PhabricatorEnv::getEnvConfig( 92 93 'differential.require-test-plan-field'); ··· 180 181 ->setUseEdgeTransactions(true) 181 182 ->setTransactionType( 182 183 DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE) 183 - ->setCommentActionLabel(pht('Edit Reviewers')) 184 + ->setCommentActionLabel(pht('Change Reviewers')) 184 185 ->setDescription(pht('Reviewers for this revision.')) 185 186 ->setConduitDescription(pht('Change the reviewers for this revision.')) 186 187 ->setConduitTypeDescription(pht('New reviewers.')) ··· 211 212 ->setConduitDescription(pht('Change associated tasks.')) 212 213 ->setConduitTypeDescription(pht('List of tasks.')) 213 214 ->setValue(array()); 215 + 216 + $actions = DifferentialRevisionActionTransaction::loadAllActions(); 217 + foreach ($actions as $key => $action) { 218 + $fields[] = $action->newEditField($object, $viewer); 219 + } 214 220 215 221 return $fields; 216 222 }
+5
src/applications/differential/storage/DifferentialRevision.php
··· 442 442 return DifferentialRevisionStatus::isClosedStatus($this->getStatus()); 443 443 } 444 444 445 + public function isAbandoned() { 446 + $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED; 447 + return ($this->getStatus() == $status_abandoned); 448 + } 449 + 445 450 public function getStatusIcon() { 446 451 $map = array( 447 452 ArcanistDifferentialRevisionStatus::NEEDS_REVIEW
+67
src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionAbandonTransaction 4 + extends DifferentialRevisionActionTransaction { 5 + 6 + const TRANSACTIONTYPE = 'differential.revision.abandon'; 7 + const ACTIONKEY = 'abandon'; 8 + 9 + protected function getRevisionActionLabel() { 10 + return pht('Abandon Revision'); 11 + } 12 + 13 + protected function getRevisionActionDescription() { 14 + return pht('This revision will be abandoned and closed.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-plane'; 19 + } 20 + 21 + public function getColor() { 22 + return 'indigo'; 23 + } 24 + 25 + public function generateOldValue($object) { 26 + return $object->isAbandoned(); 27 + } 28 + 29 + public function applyInternalEffects($object, $value) { 30 + $object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED); 31 + } 32 + 33 + protected function validateAction($object, PhabricatorUser $viewer) { 34 + if ($object->isClosed()) { 35 + throw new Exception( 36 + pht( 37 + 'You can not abandon this revision because it has already been '. 38 + 'closed. Only open revisions can be abandoned.')); 39 + } 40 + 41 + $config_key = 'differential.always-allow-abandon'; 42 + if (!PhabricatorEnv::getEnvConfig($config_key)) { 43 + if (!$this->isViewerRevisionAuthor($object, $viewer)) { 44 + throw new Exception( 45 + pht( 46 + 'You can not abandon this revision because you are not the '. 47 + 'author. You can only abandon revisions you own. You can change '. 48 + 'this behavior by adjusting the "%s" setting in Config', 49 + $config_key)); 50 + } 51 + } 52 + } 53 + 54 + public function getTitle() { 55 + return pht( 56 + '%s abandoned this revision.', 57 + $this->renderAuthor()); 58 + } 59 + 60 + public function getTitleForFeed() { 61 + return pht( 62 + '%s abandoned %s.', 63 + $this->renderAuthor(), 64 + $this->renderObject()); 65 + } 66 + 67 + }
+88
src/applications/differential/xaction/DifferentialRevisionActionTransaction.php
··· 1 + <?php 2 + 3 + abstract class DifferentialRevisionActionTransaction 4 + extends DifferentialRevisionTransactionType { 5 + 6 + final public function getRevisionActionKey() { 7 + return $this->getPhobjectClassConstant('ACTIONKEY', 32); 8 + } 9 + 10 + public function isActionAvailable($object, PhabricatorUser $viewer) { 11 + try { 12 + $this->validateAction($object, $viewer); 13 + return true; 14 + } catch (Exception $ex) { 15 + return false; 16 + } 17 + } 18 + 19 + abstract protected function validateAction($object, PhabricatorUser $viewer); 20 + abstract protected function getRevisionActionLabel(); 21 + 22 + protected function getRevisionActionDescription() { 23 + return null; 24 + } 25 + 26 + public static function loadAllActions() { 27 + return id(new PhutilClassMapQuery()) 28 + ->setAncestorClass(__CLASS__) 29 + ->setUniqueMethod('getRevisionActionKey') 30 + ->execute(); 31 + } 32 + 33 + protected function isViewerRevisionAuthor( 34 + DifferentialRevision $revision, 35 + PhabricatorUser $viewer) { 36 + 37 + if (!$viewer->getPHID()) { 38 + return false; 39 + } 40 + 41 + return ($viewer->getPHID() === $revision->getAuthorPHID()); 42 + } 43 + 44 + public function newEditField( 45 + DifferentialRevision $revision, 46 + PhabricatorUser $viewer) { 47 + 48 + $field = id(new PhabricatorApplyEditField()) 49 + ->setKey($this->getRevisionActionKey()) 50 + ->setTransactionType($this->getTransactionTypeConstant()) 51 + ->setValue(true); 52 + 53 + if ($this->isActionAvailable($revision, $viewer)) { 54 + $label = $this->getRevisionActionLabel(); 55 + if ($label !== null) { 56 + $field->setCommentActionLabel($label); 57 + 58 + $description = $this->getRevisionActionDescription(); 59 + $field->setActionDescription($description); 60 + } 61 + } 62 + 63 + return $field; 64 + } 65 + 66 + public function validateTransactions($object, array $xactions) { 67 + $errors = array(); 68 + $actor = $this->getActor(); 69 + 70 + $action_exception = null; 71 + try { 72 + $this->validateAction($object, $actor); 73 + } catch (Exception $ex) { 74 + $action_exception = $ex; 75 + } 76 + 77 + foreach ($xactions as $xaction) { 78 + if ($action_exception) { 79 + $errors[] = $this->newInvalidError( 80 + $action_exception->getMessage(), 81 + $xaction); 82 + } 83 + } 84 + 85 + return $errors; 86 + } 87 + 88 + }
+62
src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionReclaimTransaction 4 + extends DifferentialRevisionActionTransaction { 5 + 6 + const TRANSACTIONTYPE = 'differential.revision.reclaim'; 7 + const ACTIONKEY = 'reclaim'; 8 + 9 + protected function getRevisionActionLabel() { 10 + return pht('Reclaim Revision'); 11 + } 12 + 13 + protected function getRevisionActionDescription() { 14 + return pht('This revision will be reclaimed and reopened.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-bullhorn'; 19 + } 20 + 21 + public function getColor() { 22 + return 'sky'; 23 + } 24 + 25 + public function generateOldValue($object) { 26 + return !$object->isAbandoned(); 27 + } 28 + 29 + public function applyInternalEffects($object, $value) { 30 + $object->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW); 31 + } 32 + 33 + protected function validateAction($object, PhabricatorUser $viewer) { 34 + if (!$object->isAbandoned()) { 35 + throw new Exception( 36 + pht( 37 + 'You can not reclaim this revision because it has not been '. 38 + 'abandoned. Only abandoned revisions can be reclaimed.')); 39 + } 40 + 41 + if (!$this->isViewerRevisionAuthor($object, $viewer)) { 42 + throw new Exception( 43 + pht( 44 + 'You can not reclaim this revision because you are not the '. 45 + 'revision author. You can only reclaim revisions you own.')); 46 + } 47 + } 48 + 49 + public function getTitle() { 50 + return pht( 51 + '%s reclaimed this revision.', 52 + $this->renderAuthor()); 53 + } 54 + 55 + public function getTitleForFeed() { 56 + return pht( 57 + '%s reclaimed %s.', 58 + $this->renderAuthor(), 59 + $this->renderObject()); 60 + } 61 + 62 + }
+28
src/applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineStaticCommentAction 4 + extends PhabricatorEditEngineCommentAction { 5 + 6 + private $description; 7 + 8 + public function setDescription($description) { 9 + $this->description = $description; 10 + return $this; 11 + } 12 + 13 + public function getDescription() { 14 + return $this->description; 15 + } 16 + 17 + public function getPHUIXControlType() { 18 + return 'static'; 19 + } 20 + 21 + public function getPHUIXControlSpecification() { 22 + return array( 23 + 'value' => $this->getValue(), 24 + 'description' => $this->getDescription(), 25 + ); 26 + } 27 + 28 + }
+40
src/applications/transactions/editfield/PhabricatorApplyEditField.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplyEditField 4 + extends PhabricatorEditField { 5 + 6 + private $actionDescription; 7 + 8 + protected function newControl() { 9 + return null; 10 + } 11 + 12 + public function setActionDescription($action_description) { 13 + $this->actionDescription = $action_description; 14 + return $this; 15 + } 16 + 17 + public function getActionDescription() { 18 + return $this->actionDescription; 19 + } 20 + 21 + protected function newHTTPParameterType() { 22 + return new AphrontBoolHTTPParameterType(); 23 + } 24 + 25 + protected function newConduitParameterType() { 26 + return new ConduitBoolParameterType(); 27 + } 28 + 29 + public function shouldGenerateTransactionsFromSubmit() { 30 + // This type of edit field just applies a prebuilt action, like "Accept 31 + // Revision", and can not be submitted as part of an "Edit Object" form. 32 + return false; 33 + } 34 + 35 + protected function newCommentAction() { 36 + return id(new PhabricatorEditEngineStaticCommentAction()) 37 + ->setDescription($this->getActionDescription()); 38 + } 39 + 40 + }
+5
webroot/rsrc/css/phui/phui-form-view.css
··· 545 545 .device-desktop .aphront-form-error .phui-icon-view:hover { 546 546 color: {$red}; 547 547 } 548 + 549 + .phui-form-static-action { 550 + padding: 4px; 551 + color: {$bluetext}; 552 + }
+22
webroot/rsrc/js/phuix/PHUIXFormControl.js
··· 47 47 case 'optgroups': 48 48 input = this._newOptgroups(spec); 49 49 break; 50 + case 'static': 51 + input = this._newStatic(spec); 52 + break; 50 53 default: 51 54 // TODO: Default or better error? 52 55 JX.$E('Bad Input Type'); ··· 168 171 }, 169 172 set: function(value) { 170 173 node.value = value; 174 + } 175 + }; 176 + }, 177 + 178 + _newStatic: function(spec) { 179 + var node = JX.$N( 180 + 'div', 181 + { 182 + className: 'phui-form-static-action' 183 + }, 184 + spec.description || ''); 185 + 186 + return { 187 + node: node, 188 + get: function() { 189 + return true; 190 + }, 191 + set: function() { 192 + return; 171 193 } 172 194 }; 173 195 },