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

Make "Assign / Claim" stacked action work properly in Maniphest

Summary:
Ref T9132. This is kind of a mess because the tokenizer rewrite left rendering tokenizers in Javascript a little rough. This causes bugs like icons not showing up on tokens in the "Policy" dialog, which there's a task for somewhere I think.

I think I've fixed it enough that the beahavior is now correct (i.e., icons show up properly), but some of the code is a bit iffy. I'll eventually clean this up properly, but it's fairly well contained for now.

Test Plan:
- Reassigned a task.
- Put a task up for grabs.
- No reassign on closed tasks.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9132

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

+249 -103
+21 -21
resources/celerity/map.php
··· 8 8 return array( 9 9 'names' => array( 10 10 'core.pkg.css' => '4d47b0a9', 11 - 'core.pkg.js' => '47dc9ebb', 11 + 'core.pkg.js' => '21eccc42', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '2de124c9', 14 14 'differential.pkg.js' => '6223dd9d', ··· 456 456 'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f', 457 457 'rsrc/js/core/MultirowRowManager.js' => 'b5d57730', 458 458 'rsrc/js/core/Notification.js' => 'ccf1cbf8', 459 - 'rsrc/js/core/Prefab.js' => '6920d200', 459 + 'rsrc/js/core/Prefab.js' => '2381d07a', 460 460 'rsrc/js/core/ShapedRequest.js' => '7cbe244b', 461 461 'rsrc/js/core/TextAreaUtils.js' => '5c93c52c', 462 462 'rsrc/js/core/Title.js' => 'df5e11d2', ··· 506 506 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 507 507 'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262', 508 508 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', 509 - 'rsrc/js/phuix/PHUIXFormControl.js' => '7e1dc09e', 509 + 'rsrc/js/phuix/PHUIXFormControl.js' => '1adf0d30', 510 510 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 511 511 ), 512 512 'symbols' => array( ··· 757 757 'phabricator-notification-menu-css' => 'f31c0bde', 758 758 'phabricator-object-selector-css' => '85ee8ce6', 759 759 'phabricator-phtize' => 'd254d646', 760 - 'phabricator-prefab' => '6920d200', 760 + 'phabricator-prefab' => '2381d07a', 761 761 'phabricator-remarkup-css' => 'b1c10368', 762 762 'phabricator-search-results-css' => '7dea472c', 763 763 'phabricator-shaped-request' => '7cbe244b', ··· 831 831 'phuix-action-list-view' => 'b5c256b8', 832 832 'phuix-action-view' => '8cf6d262', 833 833 'phuix-dropdown-menu' => 'bd4c8dca', 834 - 'phuix-form-control-view' => '7e1dc09e', 834 + 'phuix-form-control-view' => '1adf0d30', 835 835 'phuix-icon-view' => 'bff6884b', 836 836 'policy-css' => '957ea14c', 837 837 'policy-edit-css' => '815c66f7', ··· 946 946 'javelin-util', 947 947 'javelin-reactor-node-calmer', 948 948 ), 949 + '1adf0d30' => array( 950 + 'javelin-install', 951 + 'javelin-dom', 952 + ), 949 953 '1ae869f2' => array( 950 954 'javelin-install', 951 955 'javelin-util', ··· 1003 1007 'javelin-workflow', 1004 1008 'javelin-util', 1005 1009 ), 1010 + '2381d07a' => array( 1011 + 'javelin-install', 1012 + 'javelin-util', 1013 + 'javelin-dom', 1014 + 'javelin-typeahead', 1015 + 'javelin-tokenizer', 1016 + 'javelin-typeahead-preloaded-source', 1017 + 'javelin-typeahead-ondemand-source', 1018 + 'javelin-dom', 1019 + 'javelin-stratcom', 1020 + 'javelin-util', 1021 + ), 1006 1022 '246dc085' => array( 1007 1023 'javelin-behavior', 1008 1024 'javelin-stratcom', ··· 1304 1320 '6882e80a' => array( 1305 1321 'javelin-dom', 1306 1322 ), 1307 - '6920d200' => array( 1308 - 'javelin-install', 1309 - 'javelin-util', 1310 - 'javelin-dom', 1311 - 'javelin-typeahead', 1312 - 'javelin-tokenizer', 1313 - 'javelin-typeahead-preloaded-source', 1314 - 'javelin-typeahead-ondemand-source', 1315 - 'javelin-dom', 1316 - 'javelin-stratcom', 1317 - 'javelin-util', 1318 - ), 1319 1323 '69adf288' => array( 1320 1324 'javelin-install', 1321 1325 ), ··· 1429 1433 'phuix-action-list-view', 1430 1434 'phuix-action-view', 1431 1435 'javelin-workflow', 1432 - ), 1433 - '7e1dc09e' => array( 1434 - 'javelin-install', 1435 - 'javelin-dom', 1436 1436 ), 1437 1437 '7e41274a' => array( 1438 1438 'javelin-install',
+5 -1
src/__phutil_library_map__.php
··· 2087 2087 'PhabricatorDataNotAttachedException' => 'infrastructure/storage/lisk/PhabricatorDataNotAttachedException.php', 2088 2088 'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php', 2089 2089 'PhabricatorDatasourceEditField' => 'applications/transactions/editfield/PhabricatorDatasourceEditField.php', 2090 + 'PhabricatorDatasourceEditType' => 'applications/transactions/edittype/PhabricatorDatasourceEditType.php', 2090 2091 'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php', 2091 2092 'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php', 2092 2093 'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php', ··· 2590 2591 'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php', 2591 2592 'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php', 2592 2593 'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php', 2594 + 'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php', 2593 2595 'PhabricatorPHIDResolver' => 'applications/phid/resolver/PhabricatorPHIDResolver.php', 2594 2596 'PhabricatorPHIDType' => 'applications/phid/type/PhabricatorPHIDType.php', 2595 2597 'PhabricatorPHIDTypeTestCase' => 'applications/phid/type/__tests__/PhabricatorPHIDTypeTestCase.php', ··· 6202 6204 'PhabricatorDataNotAttachedException' => 'Exception', 6203 6205 'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck', 6204 6206 'PhabricatorDatasourceEditField' => 'PhabricatorTokenizerEditField', 6207 + 'PhabricatorDatasourceEditType' => 'PhabricatorPHIDListEditType', 6205 6208 'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel', 6206 6209 'PhabricatorDebugController' => 'PhabricatorController', 6207 6210 'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', ··· 6228 6231 'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants', 6229 6232 'PhabricatorEdgeConstants' => 'Phobject', 6230 6233 'PhabricatorEdgeCycleException' => 'Exception', 6231 - 'PhabricatorEdgeEditType' => 'PhabricatorEditType', 6234 + 'PhabricatorEdgeEditType' => 'PhabricatorPHIDListEditType', 6232 6235 'PhabricatorEdgeEditor' => 'Phobject', 6233 6236 'PhabricatorEdgeGraph' => 'AbstractDirectedGraph', 6234 6237 'PhabricatorEdgeQuery' => 'PhabricatorQuery', ··· 6777 6780 'PhabricatorPHID' => 'Phobject', 6778 6781 'PhabricatorPHIDConstants' => 'Phobject', 6779 6782 'PhabricatorPHIDListEditField' => 'PhabricatorEditField', 6783 + 'PhabricatorPHIDListEditType' => 'PhabricatorEditType', 6780 6784 'PhabricatorPHIDResolver' => 'Phobject', 6781 6785 'PhabricatorPHIDType' => 'Phobject', 6782 6786 'PhabricatorPHIDTypeTestCase' => 'PhutilTestCase',
+11 -3
src/applications/maniphest/editor/ManiphestEditEngine.php
··· 61 61 $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); 62 62 63 63 // TODO: Restore these or toss them: 64 - // - Default owner to viewer. 65 - // - Don't show "change owner" for closed tasks. 66 64 // - When closing an unassigned task, assign the closing user. 67 65 // - Make sure implicit CCs on actions are working reasonably. 68 66 69 67 if ($object->isClosed()) { 70 68 $priority_label = null; 69 + $owner_label = null; 71 70 $default_status = ManiphestTaskStatus::getDefaultStatus(); 72 71 } else { 73 72 $priority_label = pht('Change Priority'); 73 + $owner_label = pht('Assign / Claim'); 74 74 $default_status = ManiphestTaskStatus::getDefaultClosedStatus(); 75 + } 76 + 77 + if ($object->getOwnerPHID()) { 78 + $owner_value = array($object->getOwnerPHID()); 79 + } else { 80 + $owner_value = array($this->getViewer()->getPHID()); 75 81 } 76 82 77 83 return array( ··· 97 103 ->setLabel(pht('Assigned To')) 98 104 ->setDescription(pht('User who is responsible for the task.')) 99 105 ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 100 - ->setSingleValue($object->getOwnerPHID()), 106 + ->setSingleValue($object->getOwnerPHID()) 107 + ->setCommentActionLabel($owner_label) 108 + ->setCommentActionDefaultValue($owner_value), 101 109 id(new PhabricatorSelectEditField()) 102 110 ->setKey('priority') 103 111 ->setLabel(pht('Priority'))
+12 -4
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 1049 1049 foreach ($fields as $field) { 1050 1050 $types = $field->getCommentEditTypes(); 1051 1051 foreach ($types as $type) { 1052 - $type_map[$type->getEditType()] = $type; 1052 + $type_map[$type->getEditType()] = array( 1053 + 'type' => $type, 1054 + 'field' => $field, 1055 + ); 1053 1056 } 1054 1057 } 1055 1058 ··· 1060 1063 continue; 1061 1064 } 1062 1065 1063 - $edit_type = idx($type_map, $type); 1064 - if (!$edit_type) { 1066 + $spec = idx($type_map, $type); 1067 + if (!$spec) { 1065 1068 continue; 1066 1069 } 1067 1070 1071 + $edit_type = $spec['type']; 1072 + $field = $spec['field']; 1073 + 1074 + $field->readValueFromComment($action); 1075 + 1068 1076 $type_xactions = $edit_type->generateTransactions( 1069 1077 $template, 1070 1078 array( 1071 - 'value' => idx($action, 'value'), 1079 + 'value' => $field->getValueForTransaction(), 1072 1080 )); 1073 1081 foreach ($type_xactions as $type_xaction) { 1074 1082 $xactions[] = $type_xaction;
+9 -1
src/applications/transactions/editfield/PhabricatorEditField.php
··· 182 182 return $this->commentActionLabel; 183 183 } 184 184 185 - 186 185 protected function newControl() { 187 186 throw new PhutilMethodNotImplementedException(); 188 187 } ··· 320 319 break; 321 320 } 322 321 return $this; 322 + } 323 + 324 + public function readValueFromComment($action) { 325 + $this->value = $this->getValueFromComment(idx($action, 'value')); 326 + return $this; 327 + } 328 + 329 + protected function getValueFromComment($value) { 330 + return $value; 323 331 } 324 332 325 333 public function getAllReadValueFromRequestKeys() {
+2 -6
src/applications/transactions/editfield/PhabricatorPHIDListEditField.php
··· 92 92 return new PhabricatorEdgeEditType(); 93 93 } 94 94 95 - $type = parent::newEditType(); 96 - 97 - if ($this->getIsSingleValue()) { 98 - $type->setValueType('phid'); 99 - } 100 - 95 + $type = new PhabricatorDatasourceEditType(); 96 + $type->setIsSingleValue($this->getIsSingleValue()); 101 97 return $type; 102 98 } 103 99
+34 -17
src/applications/transactions/editfield/PhabricatorTokenizerEditField.php
··· 3 3 abstract class PhabricatorTokenizerEditField 4 4 extends PhabricatorPHIDListEditField { 5 5 6 + private $commentActionDefaultValue; 7 + 6 8 abstract protected function newDatasource(); 9 + 10 + public function setCommentActionDefaultValue(array $default) { 11 + $this->commentActionDefaultValue = $default; 12 + return $this; 13 + } 14 + 15 + public function getCommentActionDefaultValue() { 16 + return $this->commentActionDefaultValue; 17 + } 7 18 8 19 protected function newControl() { 9 20 $control = id(new AphrontFormTokenizerControl()) ··· 28 39 protected function newEditType() { 29 40 $type = parent::newEditType(); 30 41 31 - if ($this->getUseEdgeTransactions()) { 32 - $datasource = $this->newDatasource() 33 - ->setViewer($this->getViewer()); 34 - $type->setDatasource($datasource); 35 - } 42 + $datasource = $this->newDatasource() 43 + ->setViewer($this->getViewer()); 44 + $type->setDatasource($datasource); 36 45 37 46 return $type; 38 47 } 39 48 40 49 public function getCommentEditTypes() { 41 - if (!$this->getUseEdgeTransactions()) { 42 - return parent::getCommentEditTypes(); 50 + $label = $this->getCommentActionLabel(); 51 + if ($label === null) { 52 + return array(); 43 53 } 44 54 45 55 $transaction_type = $this->getTransactionType(); ··· 47 57 return array(); 48 58 } 49 59 50 - $label = $this->getCommentActionLabel(); 51 - if ($label === null) { 52 - return array(); 60 + if ($this->getUseEdgeTransactions()) { 61 + $type_key = $this->getEditTypeKey(); 62 + $base = $this->getEditType(); 63 + 64 + $add = id(clone $base) 65 + ->setEditType($type_key.'.add') 66 + ->setEdgeOperation('+') 67 + ->setLabel($label); 68 + 69 + return array($add); 53 70 } 54 71 55 - $type_key = $this->getEditTypeKey(); 56 - $base = $this->getEditType(); 72 + $edit = $this->getEditType() 73 + ->setLabel($label); 57 74 58 - $add = id(clone $base) 59 - ->setEditType($type_key.'.add') 60 - ->setEdgeOperation('+') 61 - ->setLabel($label); 75 + $default = $this->getCommentActionDefaultValue(); 76 + if ($default) { 77 + $edit->setDefaultValue($default); 78 + } 62 79 63 - return array($add); 80 + return array($edit); 64 81 } 65 82 66 83 }
+22
src/applications/transactions/edittype/PhabricatorDatasourceEditType.php
··· 1 + <?php 2 + 3 + final class PhabricatorDatasourceEditType 4 + extends PhabricatorPHIDListEditType { 5 + 6 + public function generateTransactions( 7 + PhabricatorApplicationTransaction $template, 8 + array $spec) { 9 + 10 + $value = idx($spec, 'value'); 11 + 12 + $xaction = $this->newTransaction($template) 13 + ->setNewValue($value); 14 + 15 + return array($xaction); 16 + } 17 + 18 + public function getValueDescription() { 19 + return '?'; 20 + } 21 + 22 + }
+2 -44
src/applications/transactions/edittype/PhabricatorEdgeEditType.php
··· 1 1 <?php 2 2 3 - final class PhabricatorEdgeEditType extends PhabricatorEditType { 3 + final class PhabricatorEdgeEditType 4 + extends PhabricatorPHIDListEditType { 4 5 5 6 private $edgeOperation; 6 7 private $valueDescription; 7 - private $datasource; 8 8 9 9 public function setEdgeOperation($edge_operation) { 10 10 $this->edgeOperation = $edge_operation; ··· 15 15 return $this->edgeOperation; 16 16 } 17 17 18 - public function setDatasource($datasource) { 19 - $this->datasource = $datasource; 20 - return $this; 21 - } 22 - 23 - public function getDatasource() { 24 - return $this->datasource; 25 - } 26 - 27 - public function getValueType() { 28 - return 'list<phid>'; 29 - } 30 - 31 18 public function generateTransactions( 32 19 PhabricatorApplicationTransaction $template, 33 20 array $spec) { ··· 54 41 55 42 public function getValueDescription() { 56 43 return $this->valueDescription; 57 - } 58 - 59 - public function getPHUIXControlType() { 60 - $datasource = $this->getDatasource(); 61 - 62 - if (!$datasource) { 63 - return null; 64 - } 65 - 66 - return 'tokenizer'; 67 - } 68 - 69 - public function getPHUIXControlSpecification() { 70 - $datasource = $this->getDatasource(); 71 - 72 - if (!$datasource) { 73 - return null; 74 - } 75 - 76 - $template = new AphrontTokenizerTemplateView(); 77 - 78 - return array( 79 - 'markup' => $template->render(), 80 - 'config' => array( 81 - 'src' => $datasource->getDatasourceURI(), 82 - 'browseURI' => $datasource->getBrowseURI(), 83 - 'placeholder' => $datasource->getPlaceholderText(), 84 - ), 85 - ); 86 44 } 87 45 88 46 }
+90
src/applications/transactions/edittype/PhabricatorPHIDListEditType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorPHIDListEditType 4 + extends PhabricatorEditType { 5 + 6 + private $datasource; 7 + private $isSingleValue; 8 + private $defaultValue; 9 + 10 + public function setDatasource(PhabricatorTypeaheadDatasource $datasource) { 11 + $this->datasource = $datasource; 12 + return $this; 13 + } 14 + 15 + public function getDatasource() { 16 + return $this->datasource; 17 + } 18 + 19 + public function setIsSingleValue($is_single_value) { 20 + $this->isSingleValue = $is_single_value; 21 + return $this; 22 + } 23 + 24 + public function getIsSingleValue() { 25 + return $this->isSingleValue; 26 + } 27 + 28 + public function setDefaultValue(array $default_value) { 29 + $this->defaultValue = $default_value; 30 + return $this; 31 + } 32 + 33 + public function getDefaultValue() { 34 + return $this->defaultValue; 35 + } 36 + 37 + public function getValueType() { 38 + if ($this->getIsSingleValue()) { 39 + return 'phid'; 40 + } else { 41 + return 'list<phid>'; 42 + } 43 + } 44 + 45 + public function getPHUIXControlType() { 46 + $datasource = $this->getDatasource(); 47 + 48 + if (!$datasource) { 49 + return null; 50 + } 51 + 52 + return 'tokenizer'; 53 + } 54 + 55 + public function getPHUIXControlSpecification() { 56 + $datasource = $this->getDatasource(); 57 + 58 + if (!$datasource) { 59 + return null; 60 + } 61 + 62 + $template = new AphrontTokenizerTemplateView(); 63 + 64 + if ($this->getIsSingleValue()) { 65 + $limit = 1; 66 + } else { 67 + $limit = null; 68 + } 69 + 70 + $default = $this->getDefaultValue(); 71 + if ($default) { 72 + $value = $datasource->getWireTokens($default); 73 + } else { 74 + $value = array(); 75 + } 76 + 77 + return array( 78 + 'markup' => $template->render(), 79 + 'config' => array( 80 + 'src' => $datasource->getDatasourceURI(), 81 + 'browseURI' => $datasource->getBrowseURI(), 82 + 'placeholder' => $datasource->getPlaceholderText(), 83 + 'limit' => $limit, 84 + ), 85 + 'value' => $value, 86 + ); 87 + } 88 + 89 + 90 + }
+21
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 458 458 return $tokens; 459 459 } 460 460 461 + public function getWireTokens(array $values) { 462 + // TODO: This is a bit hacky for now: we're sort of generating wire 463 + // results, rendering them, then reverting them back to wire results. This 464 + // is pretty silly. It would probably be much cleaner to make 465 + // renderTokens() call this method instead, then render from the result 466 + // structure. 467 + $rendered = $this->renderTokens($values); 468 + 469 + $tokens = array(); 470 + foreach ($rendered as $key => $render) { 471 + $tokens[$key] = id(new PhabricatorTypeaheadResult()) 472 + ->setPHID($key) 473 + ->setIcon($render->getIcon()) 474 + ->setColor($render->getColor()) 475 + ->setDisplayName($render->getValue()) 476 + ->setTokenType($render->getTokenType()); 477 + } 478 + 479 + return mpull($tokens, 'getWireFormat'); 480 + } 481 + 461 482 }
+10 -1
webroot/rsrc/js/core/Prefab.js
··· 173 173 var tokenizer = new JX.Tokenizer(root); 174 174 tokenizer.setTypeahead(typeahead); 175 175 tokenizer.setRenderTokenCallback(function(value, key, container) { 176 - var result = datasource.getResult(key); 176 + var result; 177 + if (value && (typeof value == 'object') && ('id' in value)) { 178 + // TODO: In this case, we've been passed the decoded wire format 179 + // dictionary directly. Token rendering is kind of a huge mess that 180 + // should be cleaned up and made more consistent. Just force our 181 + // way through for now. 182 + result = value; 183 + } else { 184 + result = datasource.getResult(key); 185 + } 177 186 178 187 var icon; 179 188 var type;
+10 -5
webroot/rsrc/js/phuix/PHUIXFormControl.js
··· 118 118 spec.config); 119 119 build.tokenizer.start(); 120 120 121 + function set_value(map) { 122 + for (var k in map) { 123 + var v = JX.Prefab.transformDatasourceResults(map[k]); 124 + build.tokenizer.addToken(k, v); 125 + } 126 + } 127 + 128 + set_value(spec.value || {}); 129 + 121 130 return { 122 131 node: build.node, 123 132 get: function() { 124 133 return JX.keys(build.tokenizer.getTokens()); 125 134 }, 126 - set: function(map) { 127 - for (var k in map) { 128 - build.tokenizer.addToken(k, map[k]); 129 - } 130 - } 135 + set: set_value 131 136 }; 132 137 }, 133 138