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

Explicitly track inline comment reply threading

Summary:
Ref T1460. Track and store which comments are threaded replies to other comments, vs merely appearing on the same lines.

This doesn't actually write `hasReplies` yet, since that needs to happen when we un-draft comments on submission.

Test Plan: Made inline comments in Differential and Diffusion, including replies. Replies were marked as "Is Reply".

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T1460

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

+175 -12
+28
src/applications/audit/storage/PhabricatorAuditInlineComment.php
··· 47 47 return head(self::buildProxies($inlines)); 48 48 } 49 49 50 + public static function loadPHID($phid) { 51 + $inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere( 52 + 'phid = %s', 53 + $phid); 54 + if (!$inlines) { 55 + return null; 56 + } 57 + return head(self::buildProxies($inlines)); 58 + } 59 + 50 60 public static function loadDraftComments( 51 61 PhabricatorUser $viewer, 52 62 $commit_phid) { ··· 254 264 255 265 public function getChangesetID() { 256 266 return $this->getPathID(); 267 + } 268 + 269 + public function setReplyToCommentPHID($phid) { 270 + $this->proxy->setReplyToCommentPHID($phid); 271 + return $this; 272 + } 273 + 274 + public function getReplyToCommentPHID() { 275 + return $this->proxy->getReplyToCommentPHID(); 276 + } 277 + 278 + public function setHasReplies($has_replies) { 279 + $this->proxy->setHasReplies($has_replies); 280 + return $this; 281 + } 282 + 283 + public function getHasReplies() { 284 + return $this->proxy->getHasReplies(); 257 285 } 258 286 259 287
+6
src/applications/differential/controller/DifferentialInlineCommentEditController.php
··· 40 40 ->executeOne(); 41 41 } 42 42 43 + protected function loadCommentByPHID($phid) { 44 + return id(new DifferentialInlineCommentQuery()) 45 + ->withPHIDs(array($phid)) 46 + ->executeOne(); 47 + } 48 + 43 49 protected function loadCommentForEdit($id) { 44 50 $request = $this->getRequest(); 45 51 $user = $request->getUser();
+13
src/applications/differential/query/DifferentialInlineCommentQuery.php
··· 9 9 private $revisionIDs; 10 10 private $notDraft; 11 11 private $ids; 12 + private $phids; 12 13 private $commentIDs; 13 14 14 15 private $viewerAndChangesetIDs; ··· 27 28 28 29 public function withIDs(array $ids) { 29 30 $this->ids = $ids; 31 + return $this; 32 + } 33 + 34 + public function withPHIDs(array $phids) { 35 + $this->phids = $phids; 30 36 return $this; 31 37 } 32 38 ··· 109 115 $conn_r, 110 116 'id IN (%Ld)', 111 117 $this->ids); 118 + } 119 + 120 + if ($this->phids !== null) { 121 + $where[] = qsprintf( 122 + $conn_r, 123 + 'phid IN (%Ls)', 124 + $this->phids); 112 125 } 113 126 114 127 if ($this->viewerAndChangesetIDs) {
+19
src/applications/differential/storage/DifferentialInlineComment.php
··· 189 189 return $this; 190 190 } 191 191 192 + public function setReplyToCommentPHID($phid) { 193 + $this->proxy->setReplyToCommentPHID($phid); 194 + return $this; 195 + } 196 + 197 + public function getReplyToCommentPHID() { 198 + return $this->proxy->getReplyToCommentPHID(); 199 + } 200 + 201 + public function setHasReplies($has_replies) { 202 + $this->proxy->setHasReplies($has_replies); 203 + return $this; 204 + } 205 + 206 + public function getHasReplies() { 207 + return $this->proxy->getHasReplies(); 208 + } 209 + 210 + 192 211 /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ 193 212 194 213
+4
src/applications/diffusion/controller/DiffusionInlineCommentController.php
··· 43 43 return PhabricatorAuditInlineComment::loadID($id); 44 44 } 45 45 46 + protected function loadCommentByPHID($phid) { 47 + return PhabricatorAuditInlineComment::loadPHID($phid); 48 + } 49 + 46 50 protected function loadCommentForEdit($id) { 47 51 $request = $this->getRequest(); 48 52 $user = $request->getUser();
+39 -5
src/infrastructure/diff/PhabricatorInlineCommentController.php
··· 6 6 abstract protected function createComment(); 7 7 abstract protected function loadComment($id); 8 8 abstract protected function loadCommentForEdit($id); 9 + abstract protected function loadCommentByPHID($phid); 9 10 abstract protected function deleteComment( 10 11 PhabricatorInlineCommentInterface $inline); 11 12 abstract protected function saveComment( ··· 20 21 private $operation; 21 22 private $commentID; 22 23 private $renderer; 24 + private $replyToCommentPHID; 23 25 24 26 public function getCommentID() { 25 27 return $this->commentID; ··· 60 62 61 63 public function getRenderer() { 62 64 return $this->renderer; 65 + } 66 + 67 + public function setReplyToCommentPHID($phid) { 68 + $this->replyToCommentPHID = $phid; 69 + return $this; 70 + } 71 + 72 + public function getReplyToCommentPHID() { 73 + return $this->replyToCommentPHID; 63 74 } 64 75 65 76 public function processRequest() { ··· 114 125 115 126 $edit_dialog->addHiddenInput('id', $this->getCommentID()); 116 127 $edit_dialog->addHiddenInput('op', 'edit'); 117 - $edit_dialog->addHiddenInput('renderer', $this->getRenderer()); 118 128 119 129 $edit_dialog->appendChild( 120 130 $this->renderTextArea( ··· 136 146 ->setLineLength($this->getLineLength()) 137 147 ->setIsNewFile($this->getIsNewFile()) 138 148 ->setContent($text); 149 + 150 + if ($this->getReplyToCommentPHID()) { 151 + $inline->setReplyToCommentPHID($this->getReplyToCommentPHID()); 152 + } 153 + 139 154 $this->saveComment($inline); 140 155 141 156 return $this->buildRenderedCommentResponse( ··· 162 177 } 163 178 164 179 $edit_dialog->addHiddenInput('op', 'create'); 165 - $edit_dialog->addHiddenInput('changeset', $changeset); 166 180 $edit_dialog->addHiddenInput('is_new', $is_new); 167 181 $edit_dialog->addHiddenInput('number', $number); 168 182 $edit_dialog->addHiddenInput('length', $length); 169 - $edit_dialog->addHiddenInput('renderer', $this->getRenderer()); 170 183 171 184 $text_area = $this->renderTextArea($this->getCommentText()); 172 185 $edit_dialog->appendChild($text_area); ··· 181 194 182 195 // NOTE: This isn't necessarily a DifferentialChangeset ID, just an 183 196 // application identifier for the changeset. In Diffusion, it's a Path ID. 184 - $this->changesetID = $request->getInt('changeset'); 197 + $this->changesetID = $request->getInt('changesetID'); 185 198 186 199 $this->isNewFile = (int)$request->getBool('is_new'); 187 200 $this->isOnRight = $request->getBool('on_right'); ··· 191 204 $this->commentID = $request->getInt('id'); 192 205 $this->operation = $request->getStr('op'); 193 206 $this->renderer = $request->getStr('renderer'); 207 + $this->replyToCommentPHID = $request->getStr('replyToCommentPHID'); 208 + 209 + if ($this->getReplyToCommentPHID()) { 210 + $reply_phid = $this->getReplyToCommentPHID(); 211 + $reply_comment = $this->loadCommentByPHID($reply_phid); 212 + if (!$reply_comment) { 213 + throw new Exception( 214 + pht('Failed to load comment "%s".', $reply_phid)); 215 + } 216 + 217 + if ($reply_comment->getChangesetID() != $this->getChangesetID()) { 218 + throw new Exception( 219 + pht( 220 + 'Comment "%s" belongs to wrong changeset (%s vs %s).', 221 + $reply_phid, 222 + $reply_comment->getChangesetID(), 223 + $this->getChangesetID())); 224 + } 225 + } 194 226 } 195 227 196 228 private function buildEditDialog() { ··· 204 236 ->setIsNewFile($this->getIsNewFile()) 205 237 ->setNumber($this->getLineNumber()) 206 238 ->setLength($this->getLineLength()) 207 - ->setRenderer($this->getRenderer()); 239 + ->setRenderer($this->getRenderer()) 240 + ->setReplyToCommentPHID($this->getReplyToCommentPHID()) 241 + ->setChangesetID($this->getChangesetID()); 208 242 209 243 return $edit_dialog; 210 244 }
+6
src/infrastructure/diff/interface/PhabricatorInlineCommentInterface.php
··· 19 19 public function setLineLength($length); 20 20 public function getLineLength(); 21 21 22 + public function setReplyToCommentPHID($phid); 23 + public function getReplyToCommentPHID(); 24 + 25 + public function setHasReplies($has_replies); 26 + public function getHasReplies(); 27 + 22 28 public function setContent($content); 23 29 public function getContent(); 24 30
+12
src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
··· 76 76 77 77 $metadata = array( 78 78 'id' => $inline->getID(), 79 + 'phid' => $inline->getPHID(), 80 + 'changesetID' => $inline->getChangesetID(), 79 81 'number' => $inline->getLineNumber(), 80 82 'length' => $inline->getLineLength(), 81 83 'isNewFile' => (bool)$inline->getIsNewFile(), 82 84 'on_right' => $this->onRight, 83 85 'original' => $inline->getContent(), 86 + 'replyToCommentPHID' => $inline->getReplyToCommentPHID(), 84 87 ); 85 88 86 89 $sigil = 'differential-inline-comment'; ··· 102 105 if ($inline->isDraft() && !$is_synthetic) { 103 106 $links[] = pht('Not Submitted Yet'); 104 107 $is_draft = true; 108 + } 109 + 110 + 111 + // TODO: This stuff is nonfinal, just making it do something. 112 + if ($inline->getHasReplies()) { 113 + $links[] = pht('Has Reply'); 114 + } 115 + if ($inline->getReplyToCommentPHID()) { 116 + $links[] = pht('Is Reply'); 105 117 } 106 118 107 119 if (!$this->preview) {
+30 -2
src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php
··· 11 11 private $length; 12 12 private $renderer; 13 13 private $isNewFile; 14 + private $replyToCommentPHID; 15 + private $changesetID; 14 16 15 17 public function setIsNewFile($is_new_file) { 16 18 $this->isNewFile = $is_new_file; ··· 49 51 return $this; 50 52 } 51 53 54 + public function setReplyToCommentPHID($reply_to_phid) { 55 + $this->replyToCommentPHID = $reply_to_phid; 56 + return $this; 57 + } 58 + 59 + public function getReplyToCommentPHID() { 60 + return $this->replyToCommentPHID; 61 + } 62 + 63 + public function setChangesetID($changeset_id) { 64 + $this->changesetID = $changeset_id; 65 + return $this; 66 + } 67 + 68 + public function getChangesetID() { 69 + return $this->changesetID; 70 + } 71 + 52 72 public function setOnRight($on_right) { 53 73 $this->onRight = $on_right; 54 - $this->addHiddenInput('on_right', $on_right); 55 74 return $this; 56 75 } 57 76 ··· 114 133 } 115 134 116 135 private function renderInputs() { 136 + $inputs = $this->inputs; 117 137 $out = array(); 118 - foreach ($this->inputs as $input) { 138 + 139 + $inputs[] = array('on_right', (bool)$this->getIsOnRight()); 140 + $inputs[] = array('replyToCommentPHID', $this->getReplyToCommentPHID()); 141 + $inputs[] = array('renderer', $this->getRenderer()); 142 + $inputs[] = array('changesetID', $this->getChangesetID()); 143 + 144 + foreach ($inputs as $input) { 119 145 list($name, $value) = $input; 120 146 $out[] = phutil_tag( 121 147 'input', ··· 170 196 'class' => 'differential-inline-comment-edit', 171 197 'sigil' => 'differential-inline-comment', 172 198 'meta' => array( 199 + 'changesetID' => $this->getChangesetID(), 173 200 'on_right' => $this->getIsOnRight(), 174 201 'isNewFile' => (bool)$this->getIsNewFile(), 175 202 'number' => $this->number, 176 203 'length' => $this->length, 204 + 'replyToCommentPHID' => $this->getReplyToCommentPHID(), 177 205 ), 178 206 ), 179 207 array(
+6 -4
webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js
··· 34 34 number : this.getLineNumber(), 35 35 is_new : this.getIsNew(), 36 36 length : this.getLength(), 37 - changeset : this.getChangeset(), 37 + changesetID : this.getChangesetID(), 38 38 text : this.getText() || '', 39 - renderer: this.getRenderer() 39 + renderer: this.getRenderer(), 40 + replyToCommentPHID: this.getReplyToCommentPHID() || '', 40 41 }; 41 42 }, 42 43 _draw : function(content, exact_row) { ··· 284 285 onRight : null, 285 286 ID : null, 286 287 lineNumber : null, 287 - changeset : null, 288 + changesetID : null, 288 289 length : null, 289 290 isNew : null, 290 291 text : null, 291 292 templates : null, 292 293 originalText : null, 293 - renderer: null 294 + renderer: null, 295 + replyToCommentPHID: null 294 296 } 295 297 296 298 });
+12 -1
webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js
··· 245 245 editor = new JX.DifferentialInlineCommentEditor(config.uri) 246 246 .setTemplates(config.undo_templates) 247 247 .setOperation('new') 248 - .setChangeset(changeset) 248 + .setChangesetID(changeset) 249 249 .setLineNumber(o) 250 250 .setLength(len) 251 251 .setIsNew(isNewFile(target) ? 1 : 0) ··· 320 320 } 321 321 322 322 var original = data.original; 323 + var reply_phid = null; 323 324 if (op == 'reply') { 324 325 // If the user hit "reply", the original text is empty (a new reply), not 325 326 // the text of the comment they're replying to. 326 327 original = ''; 328 + reply_phid = data.phid; 327 329 } 328 330 331 + var changeset_root = JX.DOM.findAbove( 332 + node, 333 + 'div', 334 + 'differential-changeset'); 335 + var view = JX.ChangesetViewManager.getForNode(changeset_root); 336 + 329 337 editor = new JX.DifferentialInlineCommentEditor(config.uri) 330 338 .setTemplates(config.undo_templates) 331 339 .setOperation(op) 332 340 .setID(data.id) 341 + .setChangesetID(data.changesetID) 333 342 .setLineNumber(data.number) 334 343 .setLength(data.length) 335 344 .setOnRight(data.on_right) ··· 337 346 .setRow(row) 338 347 .setOtherRows(other_rows) 339 348 .setTable(row.parentNode) 349 + .setReplyToCommentPHID(reply_phid) 350 + .setRenderer(view.getRenderer()) 340 351 .start(); 341 352 342 353 set_link_state(true);