@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

Make server components of inline comment content handling state-oriented

Summary: Ref T13513. Introduce a formal server-side content state object so the whole state can be saved and restored to the drafts table, read from the request, etc.

Test Plan: Created and edited inlines. Reloaded drafts with edits. Submitted normal and editing comments. Grepped for affected symbols.

Maniphest Tasks: T13513

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

+207 -70
+6 -6
resources/celerity/map.php
··· 13 13 'core.pkg.js' => '845355f4', 14 14 'dark-console.pkg.js' => '187792c2', 15 15 'differential.pkg.css' => '42a2334f', 16 - 'differential.pkg.js' => 'ff8ca085', 16 + 'differential.pkg.js' => 'd0ddfb19', 17 17 'diffusion.pkg.css' => '42c75c37', 18 18 'diffusion.pkg.js' => 'a98c0bf7', 19 19 'maniphest.pkg.css' => '35995d6d', ··· 381 381 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8', 382 382 'rsrc/js/application/diff/DiffChangeset.js' => '6e5e03d2', 383 383 'rsrc/js/application/diff/DiffChangesetList.js' => 'b51ba93a', 384 - 'rsrc/js/application/diff/DiffInline.js' => '28e53a2c', 384 + 'rsrc/js/application/diff/DiffInline.js' => '6fa445ef', 385 385 'rsrc/js/application/diff/DiffPathView.js' => '8207abf9', 386 386 'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b', 387 387 'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd', ··· 776 776 'phabricator-dashboard-css' => '5a205b9d', 777 777 'phabricator-diff-changeset' => '6e5e03d2', 778 778 'phabricator-diff-changeset-list' => 'b51ba93a', 779 - 'phabricator-diff-inline' => '28e53a2c', 779 + 'phabricator-diff-inline' => '6fa445ef', 780 780 'phabricator-diff-path-view' => '8207abf9', 781 781 'phabricator-diff-tree-view' => '5d83623b', 782 782 'phabricator-drag-and-drop-file-upload' => '4370900d', ··· 1137 1137 'javelin-install', 1138 1138 'javelin-util', 1139 1139 ), 1140 - '28e53a2c' => array( 1141 - 'javelin-dom', 1142 - ), 1143 1140 '29819b75' => array( 1144 1141 'phabricator-notification', 1145 1142 'javelin-stratcom', ··· 1563 1560 'phabricator-diff-inline', 1564 1561 'phabricator-diff-path-view', 1565 1562 'phuix-button-view', 1563 + ), 1564 + '6fa445ef' => array( 1565 + 'javelin-dom', 1566 1566 ), 1567 1567 70245195 => array( 1568 1568 'javelin-behavior',
+4
src/__phutil_library_map__.php
··· 3160 3160 'PhabricatorDestructionEngineExtensionModule' => 'applications/system/engine/PhabricatorDestructionEngineExtensionModule.php', 3161 3161 'PhabricatorDeveloperConfigOptions' => 'applications/config/option/PhabricatorDeveloperConfigOptions.php', 3162 3162 'PhabricatorDeveloperPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php', 3163 + 'PhabricatorDiffInlineCommentContentState' => 'infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php', 3163 3164 'PhabricatorDiffInlineCommentQuery' => 'infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php', 3164 3165 'PhabricatorDiffPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php', 3165 3166 'PhabricatorDiffScopeEngine' => 'infrastructure/diff/PhabricatorDiffScopeEngine.php', ··· 3592 3593 'PhabricatorInfrastructureTestCase' => '__tests__/PhabricatorInfrastructureTestCase.php', 3593 3594 'PhabricatorInlineComment' => 'infrastructure/diff/interface/PhabricatorInlineComment.php', 3594 3595 'PhabricatorInlineCommentAdjustmentEngine' => 'infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php', 3596 + 'PhabricatorInlineCommentContentState' => 'infrastructure/diff/inline/PhabricatorInlineCommentContentState.php', 3595 3597 'PhabricatorInlineCommentController' => 'infrastructure/diff/PhabricatorInlineCommentController.php', 3596 3598 'PhabricatorInlineCommentInterface' => 'applications/transactions/interface/PhabricatorInlineCommentInterface.php', 3597 3599 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', ··· 9627 9629 'PhabricatorDestructionEngineExtensionModule' => 'PhabricatorConfigModule', 9628 9630 'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions', 9629 9631 'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 9632 + 'PhabricatorDiffInlineCommentContentState' => 'PhabricatorInlineCommentContentState', 9630 9633 'PhabricatorDiffInlineCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery', 9631 9634 'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 9632 9635 'PhabricatorDiffScopeEngine' => 'Phobject', ··· 10118 10121 'PhabricatorMarkupInterface', 10119 10122 ), 10120 10123 'PhabricatorInlineCommentAdjustmentEngine' => 'Phobject', 10124 + 'PhabricatorInlineCommentContentState' => 'Phobject', 10121 10125 'PhabricatorInlineCommentController' => 'PhabricatorController', 10122 10126 'PhabricatorInlineSummaryView' => 'AphrontView', 10123 10127 'PhabricatorInstructionsEditField' => 'PhabricatorEditField',
+6 -20
src/infrastructure/diff/PhabricatorInlineCommentController.php
··· 52 52 return $this->operation; 53 53 } 54 54 55 - public function getCommentText() { 56 - return $this->commentText; 57 - } 58 - 59 55 public function getLineLength() { 60 56 return $this->lineLength; 61 57 } ··· 270 266 $viewer->getPHID(), 271 267 $inline->getID()); 272 268 273 - $map = $this->getContentState(); 274 - foreach ($map as $key => $value) { 275 - $versioned_draft->setProperty($key, $value); 276 - } 269 + $map = $this->newRequestContentState($inline)->newStorageMap(); 270 + $versioned_draft->setProperty('inline.state', $map); 277 271 $versioned_draft->save(); 278 272 279 273 // We have to synchronize the draft engine after saving a versioned ··· 524 518 return (bool)$request->getBool('hasContentState'); 525 519 } 526 520 527 - private function getContentState() { 521 + private function newRequestContentState($inline) { 528 522 $request = $this->getRequest(); 529 - 530 - $comment_text = $request->getStr('text'); 531 - 532 - return array( 533 - 'inline.text' => (string)$comment_text, 534 - ); 523 + return $inline->newContentStateFromRequest($request); 535 524 } 536 525 537 526 private function updateCommentContentState(PhabricatorInlineComment $inline) { ··· 542 531 'content state.')); 543 532 } 544 533 545 - $state = $this->getContentState(); 546 - 547 - $text = $state['inline.text']; 548 - 549 - $inline->setContent($text); 534 + $state = $this->newRequestContentState($inline); 535 + $inline->setContentState($state); 550 536 } 551 537 552 538 private function saveComment(PhabricatorInlineComment $inline) {
+69
src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php
··· 1 + <?php 2 + 3 + final class PhabricatorDiffInlineCommentContentState 4 + extends PhabricatorInlineCommentContentState { 5 + 6 + private $hasSuggestion = false; 7 + private $suggestionText = ''; 8 + 9 + public function isEmptyContentState() { 10 + if (!parent::isEmptyContentState()) { 11 + return false; 12 + } 13 + 14 + if ($this->getContentHasSuggestion()) { 15 + if (strlen($this->getSuggestionText())) { 16 + return false; 17 + } 18 + } 19 + 20 + return true; 21 + } 22 + 23 + public function setContentSuggestionText($suggestion_text) { 24 + $this->suggestionText = $suggestion_text; 25 + return $this; 26 + } 27 + 28 + public function getContentSuggestionText() { 29 + return $this->suggestionText; 30 + } 31 + 32 + public function setContentHasSuggestion($has_suggestion) { 33 + $this->hasSuggestion = $has_suggestion; 34 + return $this; 35 + } 36 + 37 + public function getContentHasSuggestion() { 38 + return $this->hasSuggestion; 39 + } 40 + 41 + public function newStorageMap() { 42 + return parent::writeStorageMap() + array( 43 + 'hasSuggestion' => $this->getContentHasSuggestion(), 44 + 'suggestionText' => $this->getContentSuggestionText(), 45 + ); 46 + } 47 + 48 + public function readStorageMap(array $map) { 49 + $result = parent::readStorageMap($map); 50 + 51 + $has_suggestion = (bool)idx($map, 'hasSuggestion'); 52 + $this->setContentHasSuggestion($has_suggestion); 53 + 54 + $suggestion_text = (string)idx($map, 'suggestionText'); 55 + $this->setContentSuggestionText($suggestion_text); 56 + 57 + return $result; 58 + } 59 + 60 + protected function newStorageMapFromRequest(AphrontRequest $request) { 61 + $map = parent::newStorageMapFromRequest($request); 62 + 63 + $map['hasSuggestion'] = (bool)$request->getBool('hasSuggestion'); 64 + $map['suggestionText'] = (string)$request->getStr('suggestionText'); 65 + 66 + return $map; 67 + } 68 + 69 + }
+47
src/infrastructure/diff/inline/PhabricatorInlineCommentContentState.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorInlineCommentContentState 4 + extends Phobject { 5 + 6 + private $contentText = ''; 7 + 8 + public function setContentText($content_text) { 9 + $this->contentText = $content_text; 10 + return $this; 11 + } 12 + 13 + public function getContentText() { 14 + return $this->contentText; 15 + } 16 + 17 + public function isEmptyContentState() { 18 + return !strlen($this->getContentText()); 19 + } 20 + 21 + public function writeStorageMap() { 22 + return array( 23 + 'text' => $this->getContentText(), 24 + ); 25 + } 26 + 27 + public function readStorageMap(array $map) { 28 + $text = (string)idx($map, 'text'); 29 + $this->setContentText($text); 30 + 31 + return $this; 32 + } 33 + 34 + final public function readFromRequest(AphrontRequest $request) { 35 + $map = $this->newStorageMapFromRequest($request); 36 + return $this->readStorageMap($map); 37 + } 38 + 39 + protected function newStorageMapFromRequest(AphrontRequest $request) { 40 + $map = array(); 41 + 42 + $map['text'] = (string)$request->getStr('text'); 43 + 44 + return $map; 45 + } 46 + 47 + }
+40 -17
src/infrastructure/diff/interface/PhabricatorInlineComment.php
··· 299 299 } 300 300 301 301 public function isVoidComment(PhabricatorUser $viewer) { 302 - return !strlen($this->getContentForEdit($viewer)); 302 + return $this->getContentStateForEdit($viewer)->isEmptyContentState(); 303 303 } 304 304 305 - public function getContentForEdit(PhabricatorUser $viewer) { 306 - $content = $this->getContent(); 305 + public function getContentStateForEdit(PhabricatorUser $viewer) { 306 + $state = $this->getContentState(); 307 307 308 - if (!$this->hasVersionedDraftForViewer($viewer)) { 309 - return $content; 308 + if ($this->hasVersionedDraftForViewer($viewer)) { 309 + $versioned_draft = $this->getVersionedDraftForViewer($viewer); 310 + if ($versioned_draft) { 311 + $storage_map = $versioned_draft->getProperty('inline.state'); 312 + if (is_array($storage_map)) { 313 + $state->readStorageMap($storage_map); 314 + } 315 + } 310 316 } 311 317 312 - $versioned_draft = $this->getVersionedDraftForViewer($viewer); 313 - if (!$versioned_draft) { 314 - return $content; 315 - } 318 + return $state; 319 + } 320 + 321 + protected function newContentState() { 322 + return new PhabricatorDiffInlineCommentContentState(); 323 + } 324 + 325 + public function newContentStateFromRequest(AphrontRequest $request) { 326 + return $this->newContentState()->readFromRequest($request); 327 + } 328 + 329 + public function getContentState() { 330 + $state = $this->newContentState(); 316 331 317 - $draft_text = $versioned_draft->getProperty('inline.text'); 318 - if ($draft_text === null) { 319 - return $content; 332 + $storage = $this->getStorageObject(); 333 + $storage_map = $storage->getAttribute('inline.state'); 334 + if (is_array($storage_map)) { 335 + $state->readStorageMap($storage_map); 320 336 } 321 337 322 - return $draft_text; 338 + $state->setContentText($this->getContent()); 339 + 340 + return $state; 323 341 } 324 342 325 - public function getContentState() { 326 - return array( 327 - 'text' => $this->getContent(), 328 - ); 343 + public function setContentState(PhabricatorInlineCommentContentState $state) { 344 + $storage = $this->getStorageObject(); 345 + $storage_map = $state->newStorageMap(); 346 + $storage->setAttribute('inline.state', $storage_map); 347 + 348 + $this->setContent($state->getContentText()); 349 + 350 + return $this; 329 351 } 352 + 330 353 331 354 /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ 332 355
+3 -3
src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php
··· 218 218 // as it is currently shown to the user, not as it was stored the last 219 219 // time they clicked "Save". 220 220 221 - $draft_content = $inline->getContentForEdit($viewer); 222 - if (strlen($draft_content)) { 223 - $inline->setContent($draft_content); 221 + $draft_state = $inline->getContentStateForEdit($viewer); 222 + if (!$draft_state->isEmptyContentState()) { 223 + $inline->setContentState($draft_state); 224 224 } 225 225 } 226 226 }
-1
src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
··· 101 101 $classes[] = 'inline-comment-element'; 102 102 } 103 103 104 - $content = $inline->getContent(); 105 104 $handles = $this->handles; 106 105 107 106 $links = array();
+3 -4
src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php
··· 82 82 $viewer = $this->getViewer(); 83 83 $inline = $this->getInlineComment(); 84 84 85 - $text = $inline->getContentForEdit($viewer); 85 + $state = $inline->getContentStateForEdit($viewer); 86 86 87 87 return id(new PhabricatorRemarkupControl()) 88 88 ->setViewer($viewer) 89 - ->setSigil('differential-inline-comment-edit-textarea') 90 - ->setName('text') 91 - ->setValue($text) 89 + ->setSigil('inline-content-text') 90 + ->setValue($state->getContentText()) 92 91 ->setDisableFullScreen(true); 93 92 } 94 93
+29 -19
webroot/rsrc/js/application/diff/DiffInline.js
··· 701 701 var textareas = JX.DOM.scry( 702 702 row, 703 703 'textarea', 704 - 'differential-inline-comment-edit-textarea'); 704 + 'inline-content-text'); 705 705 if (textareas.length) { 706 706 var area = textareas[0]; 707 707 area.focus(); ··· 814 814 }, 815 815 816 816 _readFormState: function(row) { 817 - var textarea; 817 + var state = this._newContentState(); 818 + 819 + var node; 820 + 818 821 try { 819 - textarea = JX.DOM.find( 820 - row, 821 - 'textarea', 822 - 'differential-inline-comment-edit-textarea'); 822 + node = JX.DOM.find(row, 'textarea', 'inline-content-text'); 823 + state.text = node.value; 823 824 } catch (ex) { 824 - return null; 825 + // Ignore. 825 826 } 826 827 827 - return { 828 - text: textarea.value 829 - }; 828 + try { 829 + node = JX.DOM.find(row, 'textarea', 'inline-content-suggestion'); 830 + state.suggestionText = node.value; 831 + } catch (ex) { 832 + // Ignore. 833 + } 834 + 835 + return state; 830 836 }, 831 837 832 838 _onsubmitresponse: function(response) { ··· 1053 1059 } 1054 1060 }, 1055 1061 1056 - _isVoidContentState: function(state) { 1057 - return !state.text.length; 1062 + _newContentState: function() { 1063 + return { 1064 + text: '', 1065 + suggestionText: '', 1066 + hasSuggestion: true 1067 + }; 1058 1068 }, 1059 1069 1060 - _isSameContentState: function(u, v) { 1061 - return (u.text === v.text); 1070 + _isVoidContentState: function(state) { 1071 + return (!state.text.length && !state.suggestionText.length); 1062 1072 }, 1063 1073 1064 - _newContentState: function() { 1065 - return { 1066 - text: '' 1067 - }; 1074 + _isSameContentState: function(u, v) { 1075 + return ( 1076 + (u.text === v.text) && 1077 + (u.suggestionText === v.suggestionText) && 1078 + (u.hasSuggestion === v.hasSuggestion)); 1068 1079 } 1069 - 1070 1080 } 1071 1081 1072 1082 });