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

Support enabling a formal points field in Maniphest

Summary:
Ref T4427.

- New config option for labels, enabling, etc., but no UI/niceness yet.
- When enabled, add a field.
- Allow nonnegative values, including fractional values.
- EditEngine is nice and Conduit / actions basically just work with a tiny bit of extra support code.

Test Plan:
- Edited points via "Edit".
- Edited points via Conduit.
- Edited points via stacked actions.
- Tried to set "zebra" points.
- Tried to set -1 points.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4427

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

+250 -21
+6 -6
resources/celerity/map.php
··· 507 507 'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262', 508 508 'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06', 509 509 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', 510 - 'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997', 510 + 'rsrc/js/phuix/PHUIXFormControl.js' => 'a7763e11', 511 511 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 512 512 ), 513 513 'symbols' => array( ··· 840 840 'phuix-action-view' => '8cf6d262', 841 841 'phuix-autocomplete' => '9196fb06', 842 842 'phuix-dropdown-menu' => 'bd4c8dca', 843 - 'phuix-form-control-view' => '8fba1997', 843 + 'phuix-form-control-view' => 'a7763e11', 844 844 'phuix-icon-view' => 'bff6884b', 845 845 'policy-css' => '957ea14c', 846 846 'policy-edit-css' => '815c66f7', ··· 1526 1526 'javelin-stratcom', 1527 1527 'javelin-install', 1528 1528 ), 1529 - '8fba1997' => array( 1530 - 'javelin-install', 1531 - 'javelin-dom', 1532 - ), 1533 1529 '901935ef' => array( 1534 1530 'javelin-behavior', 1535 1531 'javelin-dom', ··· 1632 1628 'javelin-behavior', 1633 1629 'javelin-uri', 1634 1630 'phabricator-notification', 1631 + ), 1632 + 'a7763e11' => array( 1633 + 'javelin-install', 1634 + 'javelin-dom', 1635 1635 ), 1636 1636 'a78c0661' => array( 1637 1637 'phui-workcard-view-css',
+9 -1
src/__phutil_library_map__.php
··· 241 241 'ConduitPHIDParameterType' => 'applications/conduit/parametertype/ConduitPHIDParameterType.php', 242 242 'ConduitParameterType' => 'applications/conduit/parametertype/ConduitParameterType.php', 243 243 'ConduitPingConduitAPIMethod' => 'applications/conduit/method/ConduitPingConduitAPIMethod.php', 244 + 'ConduitPointsParameterType' => 'applications/conduit/parametertype/ConduitPointsParameterType.php', 244 245 'ConduitProjectListParameterType' => 'applications/conduit/parametertype/ConduitProjectListParameterType.php', 245 246 'ConduitQueryConduitAPIMethod' => 'applications/conduit/method/ConduitQueryConduitAPIMethod.php', 246 247 'ConduitResultSearchEngineExtension' => 'applications/conduit/query/ConduitResultSearchEngineExtension.php', ··· 1339 1340 'ManiphestTaskOpenStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php', 1340 1341 'ManiphestTaskPHIDResolver' => 'applications/maniphest/httpparametertype/ManiphestTaskPHIDResolver.php', 1341 1342 'ManiphestTaskPHIDType' => 'applications/maniphest/phid/ManiphestTaskPHIDType.php', 1343 + 'ManiphestTaskPoints' => 'applications/maniphest/constants/ManiphestTaskPoints.php', 1342 1344 'ManiphestTaskPriority' => 'applications/maniphest/constants/ManiphestTaskPriority.php', 1343 1345 'ManiphestTaskPriorityDatasource' => 'applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php', 1344 1346 'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php', ··· 2206 2208 'PhabricatorEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditEngineExtension.php', 2207 2209 'PhabricatorEditEngineExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditEngineExtensionModule.php', 2208 2210 'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php', 2211 + 'PhabricatorEditEnginePointsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php', 2209 2212 'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php', 2210 2213 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 2211 2214 'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php', ··· 2813 2816 'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php', 2814 2817 'PhabricatorPirateEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorPirateEnglishTranslation.php', 2815 2818 'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php', 2819 + 'PhabricatorPointsEditField' => 'applications/transactions/editfield/PhabricatorPointsEditField.php', 2816 2820 'PhabricatorPolicies' => 'applications/policy/constants/PhabricatorPolicies.php', 2817 2821 'PhabricatorPolicy' => 'applications/policy/storage/PhabricatorPolicy.php', 2818 2822 'PhabricatorPolicyApplication' => 'applications/policy/application/PhabricatorPolicyApplication.php', ··· 4226 4230 'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod', 4227 4231 'ConduitGetCertificateConduitAPIMethod' => 'ConduitAPIMethod', 4228 4232 'ConduitIntListParameterType' => 'ConduitListParameterType', 4229 - 'ConduitIntParameterType' => 'ConduitListParameterType', 4233 + 'ConduitIntParameterType' => 'ConduitParameterType', 4230 4234 'ConduitListParameterType' => 'ConduitParameterType', 4231 4235 'ConduitLogGarbageCollector' => 'PhabricatorGarbageCollector', 4232 4236 'ConduitMethodDoesNotExistException' => 'ConduitMethodNotFoundException', ··· 4235 4239 'ConduitPHIDParameterType' => 'ConduitParameterType', 4236 4240 'ConduitParameterType' => 'Phobject', 4237 4241 'ConduitPingConduitAPIMethod' => 'ConduitAPIMethod', 4242 + 'ConduitPointsParameterType' => 'ConduitParameterType', 4238 4243 'ConduitProjectListParameterType' => 'ConduitListParameterType', 4239 4244 'ConduitQueryConduitAPIMethod' => 'ConduitAPIMethod', 4240 4245 'ConduitResultSearchEngineExtension' => 'PhabricatorSearchEngineExtension', ··· 5508 5513 'ManiphestTaskOpenStatusDatasource' => 'PhabricatorTypeaheadDatasource', 5509 5514 'ManiphestTaskPHIDResolver' => 'PhabricatorPHIDResolver', 5510 5515 'ManiphestTaskPHIDType' => 'PhabricatorPHIDType', 5516 + 'ManiphestTaskPoints' => 'Phobject', 5511 5517 'ManiphestTaskPriority' => 'ManiphestConstants', 5512 5518 'ManiphestTaskPriorityDatasource' => 'PhabricatorTypeaheadDatasource', 5513 5519 'ManiphestTaskPriorityHeraldAction' => 'HeraldAction', ··· 6508 6514 'PhabricatorEditEngineExtension' => 'Phobject', 6509 6515 'PhabricatorEditEngineExtensionModule' => 'PhabricatorConfigModule', 6510 6516 'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController', 6517 + 'PhabricatorEditEnginePointsCommentAction' => 'PhabricatorEditEngineCommentAction', 6511 6518 'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6512 6519 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 6513 6520 'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction', ··· 7206 7213 'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController', 7207 7214 'PhabricatorPirateEnglishTranslation' => 'PhutilTranslation', 7208 7215 'PhabricatorPlatformSite' => 'PhabricatorSite', 7216 + 'PhabricatorPointsEditField' => 'PhabricatorEditField', 7209 7217 'PhabricatorPolicies' => 'PhabricatorPolicyConstants', 7210 7218 'PhabricatorPolicy' => array( 7211 7219 'PhabricatorPolicyDAO',
+1 -1
src/applications/conduit/parametertype/ConduitIntParameterType.php
··· 1 1 <?php 2 2 3 3 final class ConduitIntParameterType 4 - extends ConduitListParameterType { 4 + extends ConduitParameterType { 5 5 6 6 protected function getParameterValue(array $request, $key) { 7 7 $value = parent::getParameterValue($request, $key);
+49
src/applications/conduit/parametertype/ConduitPointsParameterType.php
··· 1 + <?php 2 + 3 + final class ConduitPointsParameterType 4 + extends ConduitParameterType { 5 + 6 + protected function getParameterValue(array $request, $key) { 7 + $value = parent::getParameterValue($request, $key); 8 + 9 + if (($value !== null) && !is_numeric($value)) { 10 + $this->raiseValidationException( 11 + $request, 12 + $key, 13 + pht('Expected numeric points value, got something else.')); 14 + } 15 + 16 + if ($value !== null) { 17 + $value = (double)$value; 18 + if ($value < 0) { 19 + $this->raiseValidationException( 20 + $request, 21 + $key, 22 + pht('Point values must be nonnegative.')); 23 + } 24 + } 25 + 26 + return $value; 27 + } 28 + 29 + protected function getParameterTypeName() { 30 + return 'points'; 31 + } 32 + 33 + protected function getParameterFormatDescriptions() { 34 + return array( 35 + pht('A nonnegative number, or null.'), 36 + ); 37 + } 38 + 39 + protected function getParameterExamples() { 40 + return array( 41 + 'null', 42 + '0', 43 + '1', 44 + '15', 45 + '0.5', 46 + ); 47 + } 48 + 49 + }
+3 -1
src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php
··· 336 336 '"Needs Triage" panel on the home page. You should adjust this if '. 337 337 'you adjust priorities using `%s`.', 338 338 'maniphest.priorities')), 339 - 339 + $this->newOption('maniphest.points', 'map<string, wild>', array()) 340 + ->setDescription( 341 + pht('PROTOTYPE! Very hot. Burns user. Do not touch!')), 340 342 ); 341 343 } 342 344
+24
src/applications/maniphest/constants/ManiphestTaskPoints.php
··· 1 + <?php 2 + 3 + final class ManiphestTaskPoints extends Phobject { 4 + 5 + public static function getIsEnabled() { 6 + $config = self::getPointsConfig(); 7 + return idx($config, 'enabled'); 8 + } 9 + 10 + public static function getPointsLabel() { 11 + $config = self::getPointsConfig(); 12 + return idx($config, 'label', pht('Points')); 13 + } 14 + 15 + public static function getPointsActionLabel() { 16 + $config = self::getPointsConfig(); 17 + return idx($config, 'action', pht('Change Points')); 18 + } 19 + 20 + private static function getPointsConfig() { 21 + return PhabricatorEnv::getEnvConfig('maniphest.points'); 22 + } 23 + 24 + }
+31 -12
src/applications/maniphest/editor/ManiphestEditEngine.php
··· 77 77 $owner_value = array($this->getViewer()->getPHID()); 78 78 } 79 79 80 - return array( 80 + $fields = array( 81 81 id(new PhabricatorHandlesEditField()) 82 82 ->setKey('parent') 83 83 ->setLabel(pht('Parent Task')) ··· 149 149 ->setValue($object->getPriority()) 150 150 ->setOptions($priority_map) 151 151 ->setCommentActionLabel(pht('Change Priority')), 152 - id(new PhabricatorRemarkupEditField()) 153 - ->setKey('description') 154 - ->setLabel(pht('Description')) 155 - ->setDescription(pht('Task description.')) 156 - ->setConduitDescription(pht('Update the task description.')) 157 - ->setConduitTypeDescription(pht('New task description.')) 158 - ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) 159 - ->setValue($object->getDescription()) 160 - ->setPreviewPanel( 161 - id(new PHUIRemarkupPreviewPanel()) 162 - ->setHeader(pht('Description Preview'))), 163 152 ); 153 + 154 + if (ManiphestTaskPoints::getIsEnabled()) { 155 + $points_label = ManiphestTaskPoints::getPointsLabel(); 156 + $action_label = ManiphestTaskPoints::getPointsActionLabel(); 157 + 158 + $fields[] = id(new PhabricatorPointsEditField()) 159 + ->setKey('points') 160 + ->setLabel($points_label) 161 + ->setDescription(pht('Point value of the task.')) 162 + ->setConduitDescription(pht('Change the task point value.')) 163 + ->setConduitTypeDescription(pht('New task point value.')) 164 + ->setTransactionType(ManiphestTransaction::TYPE_POINTS) 165 + ->setIsCopyable(true) 166 + ->setValue($object->getPoints()) 167 + ->setCommentActionLabel($action_label); 168 + } 169 + 170 + $fields[] = id(new PhabricatorRemarkupEditField()) 171 + ->setKey('description') 172 + ->setLabel(pht('Description')) 173 + ->setDescription(pht('Task description.')) 174 + ->setConduitDescription(pht('Update the task description.')) 175 + ->setConduitTypeDescription(pht('New task description.')) 176 + ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) 177 + ->setValue($object->getDescription()) 178 + ->setPreviewPanel( 179 + id(new PHUIRemarkupPreviewPanel()) 180 + ->setHeader(pht('Description Preview'))); 181 + 182 + return $fields; 164 183 } 165 184 166 185 private function getTaskStatusMap(ManiphestTask $task) {
+39
src/applications/maniphest/editor/ManiphestTransactionEditor.php
··· 29 29 $types[] = ManiphestTransaction::TYPE_PARENT; 30 30 $types[] = ManiphestTransaction::TYPE_COLUMN; 31 31 $types[] = ManiphestTransaction::TYPE_COVER_IMAGE; 32 + $types[] = ManiphestTransaction::TYPE_POINTS; 32 33 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 33 34 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 34 35 ··· 69 70 return $object->getSubpriority(); 70 71 case ManiphestTransaction::TYPE_COVER_IMAGE: 71 72 return $object->getCoverImageFilePHID(); 73 + case ManiphestTransaction::TYPE_POINTS: 74 + return $object->getPoints(); 72 75 case ManiphestTransaction::TYPE_MERGED_INTO: 73 76 case ManiphestTransaction::TYPE_MERGED_FROM: 74 77 return null; ··· 100 103 case ManiphestTransaction::TYPE_PARENT: 101 104 case ManiphestTransaction::TYPE_COLUMN: 102 105 return $xaction->getNewValue(); 106 + case ManiphestTransaction::TYPE_POINTS: 107 + $value = $xaction->getNewValue(); 108 + if (!strlen($value)) { 109 + $value = null; 110 + } 111 + if ($value !== null) { 112 + $value = (double)$value; 113 + } 114 + return $value; 103 115 } 104 116 } 105 117 ··· 190 202 191 203 $object->setProperty('cover.filePHID', $file->getPHID()); 192 204 $object->setProperty('cover.thumbnailPHID', $xform->getPHID()); 205 + return; 206 + case ManiphestTransaction::TYPE_POINTS: 207 + $object->setPoints($xaction->getNewValue()); 193 208 return; 194 209 case ManiphestTransaction::TYPE_MERGED_FROM: 195 210 case ManiphestTransaction::TYPE_PARENT: ··· 884 899 } 885 900 } 886 901 break; 902 + 903 + case ManiphestTransaction::TYPE_POINTS: 904 + foreach ($xactions as $xaction) { 905 + $new = $xaction->getNewValue(); 906 + if (strlen($new) && !is_numeric($new)) { 907 + $errors[] = new PhabricatorApplicationTransactionValidationError( 908 + $type, 909 + pht('Invalid'), 910 + pht('Points value must be numeric or empty.'), 911 + $xaction); 912 + continue; 913 + } 914 + 915 + if ((double)$new < 0) { 916 + $errors[] = new PhabricatorApplicationTransactionValidationError( 917 + $type, 918 + pht('Invalid'), 919 + pht('Points value must be nonnegative.'), 920 + $xaction); 921 + continue; 922 + } 923 + } 924 + break; 925 + 887 926 } 888 927 889 928 return $errors;
+33
src/applications/maniphest/storage/ManiphestTransaction.php
··· 17 17 const TYPE_PARENT = 'parent'; 18 18 const TYPE_COLUMN = 'column'; 19 19 const TYPE_COVER_IMAGE = 'cover-image'; 20 + const TYPE_POINTS = 'points'; 20 21 21 22 // NOTE: this type is deprecated. Keep it around for legacy installs 22 23 // so any transactions render correctly. ··· 166 167 case self::TYPE_COVER_IMAGE: 167 168 // At least for now, don't show these. 168 169 return true; 170 + case self::TYPE_POINTS: 171 + if (!ManiphestTaskPoints::getIsEnabled()) { 172 + return true; 173 + } 169 174 } 170 175 171 176 return parent::shouldHide(); 172 177 } 173 178 179 + public function shouldHideForMail(array $xactions) { 180 + switch ($this->getTransactionType()) { 181 + case self::TYPE_POINTS: 182 + return true; 183 + } 184 + 185 + return parent::shouldHideForMail(); 186 + } 187 + 174 188 public function shouldHideForFeed() { 175 189 switch ($this->getTransactionType()) { 176 190 case self::TYPE_UNBLOCK: ··· 181 195 return true; 182 196 } 183 197 break; 198 + case self::TYPE_POINTS: 199 + return true; 184 200 } 185 201 186 202 return parent::shouldHideForFeed(); ··· 624 640 $this->renderHandleList($new)); 625 641 break; 626 642 643 + case self::TYPE_POINTS: 644 + if ($old === null) { 645 + return pht( 646 + '%s set the point value for this task to %s.', 647 + $this->renderHandleLink($author_phid), 648 + $new); 649 + } else if ($new === null) { 650 + return pht( 651 + '%s removed the point value for this task.', 652 + $this->renderHandleLink($author_phid)); 653 + } else { 654 + return pht( 655 + '%s changed the point value for this task from %s to %s.', 656 + $this->renderHandleLink($author_phid), 657 + $old, 658 + $new); 659 + } 627 660 628 661 } 629 662
+16
src/applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEnginePointsCommentAction 4 + extends PhabricatorEditEngineCommentAction { 5 + 6 + public function getPHUIXControlType() { 7 + return 'points'; 8 + } 9 + 10 + public function getPHUIXControlSpecification() { 11 + return array( 12 + 'value' => $this->getValue(), 13 + ); 14 + } 15 + 16 + }
+17
src/applications/transactions/editfield/PhabricatorPointsEditField.php
··· 1 + <?php 2 + 3 + final class PhabricatorPointsEditField 4 + extends PhabricatorEditField { 5 + 6 + protected function newControl() { 7 + return new AphrontFormTextControl(); 8 + } 9 + 10 + protected function newConduitParameterType() { 11 + return new ConduitPointsParameterType(); 12 + } 13 + 14 + protected function newCommentAction() { 15 + return id(new PhabricatorEditEnginePointsCommentAction()); 16 + } 17 + }
+22
webroot/rsrc/js/phuix/PHUIXFormControl.js
··· 35 35 case 'select': 36 36 input = this._newSelect(spec); 37 37 break; 38 + case 'points': 39 + input = this._newPoints(spec); 40 + break; 38 41 default: 39 42 // TODO: Default or better error? 40 43 JX.$E('Bad Input Type'); ··· 148 151 spec.value, 149 152 {}, 150 153 spec.order); 154 + 155 + return { 156 + node: node, 157 + get: function() { 158 + return node.value; 159 + }, 160 + set: function(value) { 161 + node.value = value; 162 + } 163 + }; 164 + }, 165 + 166 + _newPoints: function(spec) { 167 + var attrs = { 168 + type: 'text', 169 + value: spec.value 170 + }; 171 + 172 + var node = JX.$N('input', attrs); 151 173 152 174 return { 153 175 node: node,