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

Save stacked actions in drafts, not just comments

Summary:
Ref T9132. Fixes T4580. Thhat might actually have been fixed a while ago or something since it describes a buggy/bad interaction which doesn't reproduce for me at HEAD.

This saves and restores all the stacked actions (subscribers, projects, etc) so that you don't lose anything if you close a window by accident.

Test Plan:
Added a bunch of actions in various states, reloaded the page, draft stuck around.

Submitted form, actions didn't stick around anymore.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4580, T9132

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

+153 -51
+31 -31
resources/celerity/map.php
··· 8 8 return array( 9 9 'names' => array( 10 10 'core.pkg.css' => '4d47b0a9', 11 - 'core.pkg.js' => '21eccc42', 11 + 'core.pkg.js' => '3c0f7f9b', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '2de124c9', 14 14 'differential.pkg.js' => '6223dd9d', ··· 427 427 'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43', 428 428 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 429 429 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 430 - 'rsrc/js/application/transactions/behavior-comment-actions.js' => 'f293e8a0', 430 + 'rsrc/js/application/transactions/behavior-comment-actions.js' => '6de53e91', 431 431 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 432 432 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6', 433 433 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6', ··· 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' => '2381d07a', 459 + 'rsrc/js/core/Prefab.js' => 'be38fe4e', 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' => '1adf0d30', 509 + 'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997', 510 510 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 511 511 ), 512 512 'symbols' => array( ··· 571 571 'javelin-behavior-audit-preview' => 'd835b03a', 572 572 'javelin-behavior-bulk-job-reload' => 'edf8a145', 573 573 'javelin-behavior-choose-control' => '6153c708', 574 - 'javelin-behavior-comment-actions' => 'f293e8a0', 574 + 'javelin-behavior-comment-actions' => '6de53e91', 575 575 'javelin-behavior-config-reorder-fields' => 'b6993408', 576 576 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', 577 577 'javelin-behavior-conpherence-menu' => '1d45c74d', ··· 757 757 'phabricator-notification-menu-css' => 'f31c0bde', 758 758 'phabricator-object-selector-css' => '85ee8ce6', 759 759 'phabricator-phtize' => 'd254d646', 760 - 'phabricator-prefab' => '2381d07a', 760 + 'phabricator-prefab' => 'be38fe4e', 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' => '1adf0d30', 834 + 'phuix-form-control-view' => '8fba1997', 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 - ), 953 949 '1ae869f2' => array( 954 950 'javelin-install', 955 951 'javelin-util', ··· 1005 1001 'javelin-dom', 1006 1002 'javelin-json', 1007 1003 'javelin-workflow', 1008 - 'javelin-util', 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 1004 'javelin-util', 1021 1005 ), 1022 1006 '246dc085' => array( ··· 1342 1326 'phabricator-drag-and-drop-file-upload', 1343 1327 'phabricator-textareautils', 1344 1328 ), 1329 + '6de53e91' => array( 1330 + 'javelin-behavior', 1331 + 'javelin-stratcom', 1332 + 'javelin-workflow', 1333 + 'javelin-dom', 1334 + 'phuix-form-control-view', 1335 + 'phuix-icon-view', 1336 + ), 1345 1337 '6eff08aa' => array( 1346 1338 'javelin-install', 1347 1339 'javelin-util', ··· 1526 1518 'javelin-behavior', 1527 1519 'javelin-dom', 1528 1520 'javelin-stratcom', 1521 + ), 1522 + '8fba1997' => array( 1523 + 'javelin-install', 1524 + 'javelin-dom', 1529 1525 ), 1530 1526 '9007c197' => array( 1531 1527 'javelin-behavior', ··· 1762 1758 'javelin-util', 1763 1759 'javelin-request', 1764 1760 ), 1761 + 'be38fe4e' => array( 1762 + 'javelin-install', 1763 + 'javelin-util', 1764 + 'javelin-dom', 1765 + 'javelin-typeahead', 1766 + 'javelin-tokenizer', 1767 + 'javelin-typeahead-preloaded-source', 1768 + 'javelin-typeahead-ondemand-source', 1769 + 'javelin-dom', 1770 + 'javelin-stratcom', 1771 + 'javelin-util', 1772 + ), 1765 1773 'bff6884b' => array( 1766 1774 'javelin-install', 1767 1775 'javelin-dom', ··· 1971 1979 'javelin-util', 1972 1980 'javelin-workflow', 1973 1981 'javelin-json', 1974 - ), 1975 - 'f293e8a0' => array( 1976 - 'javelin-behavior', 1977 - 'javelin-stratcom', 1978 - 'javelin-workflow', 1979 - 'javelin-dom', 1980 - 'phuix-form-control-view', 1981 - 'phuix-icon-view', 1982 1982 ), 1983 1983 'f36e01af' => array( 1984 1984 'javelin-behavior',
+9 -4
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 1025 1025 1026 1026 $comment_text = $request->getStr('comment'); 1027 1027 1028 + $actions = $request->getStr('editengine.actions'); 1029 + if ($actions) { 1030 + $actions = phutil_json_decode($actions); 1031 + } 1032 + 1028 1033 if ($is_preview) { 1029 1034 $version_key = PhabricatorVersionedDraft::KEY_VERSION; 1030 1035 $request_version = $request->getInt($version_key); ··· 1036 1041 $current_version); 1037 1042 1038 1043 // TODO: This is just a proof of concept. 1039 - $draft->setProperty('temporary.comment', $comment_text); 1040 - $draft->save(); 1044 + $draft 1045 + ->setProperty('temporary.comment', $comment_text) 1046 + ->setProperty('actions', $actions) 1047 + ->save(); 1041 1048 } 1042 1049 } 1043 1050 1044 1051 $xactions = array(); 1045 1052 1046 - $actions = $request->getStr('editengine.actions'); 1047 1053 if ($actions) { 1048 1054 $type_map = array(); 1049 1055 foreach ($fields as $field) { ··· 1056 1062 } 1057 1063 } 1058 1064 1059 - $actions = phutil_json_decode($actions); 1060 1065 foreach ($actions as $action) { 1061 1066 $type = idx($action, 'type'); 1062 1067 if (!$type) {
+1
src/applications/transactions/editfield/PhabricatorSelectEditField.php
··· 53 53 ->setPHUIXControlSpecification( 54 54 array( 55 55 'options' => $this->getOptions(), 56 + 'order' => array_keys($this->getOptions()), 56 57 'value' => $default_value, 57 58 )); 58 59
+4
src/applications/transactions/edittype/PhabricatorEditType.php
··· 104 104 return null; 105 105 } 106 106 107 + public function getCommentActionValueFromDraftValue($value) { 108 + return $value; 109 + } 110 + 107 111 }
+14
src/applications/transactions/edittype/PhabricatorPHIDListEditType.php
··· 86 86 ); 87 87 } 88 88 89 + public function getCommentActionValueFromDraftValue($value) { 90 + $datasource = $this->getDatasource(); 91 + 92 + if (!$datasource) { 93 + return array(); 94 + } 95 + 96 + if (!is_array($value)) { 97 + return array(); 98 + } 99 + 100 + return $datasource->getWireTokens($value); 101 + } 102 + 89 103 90 104 }
+26
src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
··· 204 204 if ($edit_types) { 205 205 206 206 $action_map = array(); 207 + $type_map = array(); 207 208 foreach ($edit_types as $edit_type) { 208 209 $key = $edit_type->getEditType(); 209 210 $action_map[$key] = array( ··· 212 213 'type' => $edit_type->getPHUIXControlType(), 213 214 'spec' => $edit_type->getPHUIXControlSpecification(), 214 215 ); 216 + 217 + $type_map[$key] = $edit_type; 215 218 } 216 219 217 220 $options = array(); ··· 248 251 'id' => $place_id, 249 252 ))); 250 253 254 + $draft_actions = array(); 255 + if ($versioned_draft) { 256 + $draft_actions = $versioned_draft->getProperty('actions', array()); 257 + foreach ($draft_actions as $key => $action) { 258 + $type = idx($action, 'type'); 259 + if (!$type) { 260 + unset($draft_actions[$key]); 261 + continue; 262 + } 263 + 264 + $edit_type = idx($type_map, $type); 265 + if (!$edit_type) { 266 + unset($draft_actions[$key]); 267 + continue; 268 + } 269 + 270 + $value = idx($action, 'value'); 271 + $value = $edit_type->getCommentActionValueFromDraftValue($value); 272 + $draft_actions[$key]['value'] = $value; 273 + } 274 + } 275 + 251 276 Javelin::initBehavior( 252 277 'comment-actions', 253 278 array( ··· 260 285 'actions' => $action_map, 261 286 'showPreview' => $this->getShowPreview(), 262 287 'actionURI' => $this->getAction(), 288 + 'drafts' => $draft_actions, 263 289 )); 264 290 } 265 291
+2 -2
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 469 469 $tokens = array(); 470 470 foreach ($rendered as $key => $render) { 471 471 $tokens[$key] = id(new PhabricatorTypeaheadResult()) 472 - ->setPHID($key) 472 + ->setPHID($render->getKey()) 473 473 ->setIcon($render->getIcon()) 474 474 ->setColor($render->getColor()) 475 475 ->setDisplayName($render->getValue()) 476 476 ->setTokenType($render->getTokenType()); 477 477 } 478 478 479 - return mpull($tokens, 'getWireFormat'); 479 + return mpull($tokens, 'getWireFormat', 'getPHID'); 480 480 } 481 481 482 482 }
+37 -7
webroot/rsrc/js/application/transactions/behavior-comment-actions.js
··· 19 19 var rows = {}; 20 20 21 21 JX.DOM.listen(action_node, 'change', null, function() { 22 - var options = action_node.options; 23 - var option; 22 + var option = find_option(action_node.value); 24 23 25 - var selected = action_node.value; 26 24 action_node.value = '+'; 27 25 26 + if (option) { 27 + add_row(option); 28 + } 29 + }); 30 + 31 + function find_option(key) { 32 + var options = action_node.options; 33 + var option; 34 + 28 35 for (var ii = 0; ii < options.length; ii++) { 29 36 option = options[ii]; 30 - if (option.value == selected) { 31 - add_row(option); 32 - break; 37 + if (option.value == key) { 38 + return option; 33 39 } 34 40 } 35 - }); 41 + 42 + return null; 43 + } 36 44 37 45 function add_row(option) { 38 46 var action = action_map[option.value]; ··· 62 70 }); 63 71 64 72 place_node.parentNode.insertBefore(node, place_node); 73 + 74 + return control; 65 75 } 66 76 67 77 function serialize_actions() { ··· 86 96 return data; 87 97 } 88 98 99 + function restore_draft_actions(drafts) { 100 + var draft; 101 + var option; 102 + var control; 103 + 104 + for (var ii = 0; ii < drafts.length; ii++) { 105 + draft = drafts[ii]; 106 + 107 + option = find_option(draft.type); 108 + if (!option) { 109 + continue; 110 + } 111 + 112 + control = add_row(option); 113 + control.setValue(draft.value); 114 + } 115 + } 116 + 89 117 function onresponse(response) { 90 118 var panel = JX.$(config.panelID); 91 119 if (!response.xactions.length) { ··· 124 152 JX.DOM.listen(form_node, 'shouldRefresh', null, always_trigger); 125 153 request.start(); 126 154 } 155 + 156 + restore_draft_actions(config.drafts || []); 127 157 128 158 });
+15 -3
webroot/rsrc/js/core/Prefab.js
··· 19 19 JX.install('Prefab', { 20 20 21 21 statics : { 22 - renderSelect : function(map, selected, attrs) { 22 + renderSelect : function(map, selected, attrs, order) { 23 23 var select = JX.$N('select', attrs || {}); 24 - for (var k in map) { 24 + 25 + // Callers may optionally pass "order" to force options into a specific 26 + // order. Although most browsers do retain order, maps in Javascript 27 + // aren't technically ordered. Safari, at least, will reorder maps with 28 + // numeric keys. 29 + 30 + order = order || JX.keys(map); 31 + 32 + var k; 33 + for (var ii = 0; ii < order.length; ii++) { 34 + k = order[ii]; 25 35 select.options[select.options.length] = new Option(map[k], k); 26 36 if (k == selected) { 27 37 select.value = k; 28 38 } 29 39 } 30 - select.value = select.value || JX.keys(map)[0]; 40 + 41 + select.value = select.value || order[0]; 42 + 31 43 return select; 32 44 }, 33 45
+14 -4
webroot/rsrc/js/phuix/PHUIXFormControl.js
··· 118 118 spec.config); 119 119 build.tokenizer.start(); 120 120 121 + function get_value() { 122 + return JX.keys(build.tokenizer.getTokens()); 123 + } 124 + 121 125 function set_value(map) { 126 + var tokens = get_value(); 127 + for (var ii = 0; ii < tokens.length; ii++) { 128 + build.tokenizer.removeToken(tokens[ii]); 129 + } 122 130 for (var k in map) { 123 131 var v = JX.Prefab.transformDatasourceResults(map[k]); 124 132 build.tokenizer.addToken(k, v); ··· 129 137 130 138 return { 131 139 node: build.node, 132 - get: function() { 133 - return JX.keys(build.tokenizer.getTokens()); 134 - }, 140 + get: get_value, 135 141 set: set_value 136 142 }; 137 143 }, 138 144 139 145 _newSelect: function(spec) { 140 - var node = JX.Prefab.renderSelect(spec.options, spec.value); 146 + var node = JX.Prefab.renderSelect( 147 + spec.options, 148 + spec.value, 149 + {}, 150 + spec.order); 141 151 142 152 return { 143 153 node: node,