diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1164,6 +1164,7 @@ 'PhabricatorApplicationTransactionCommentEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php', 'PhabricatorApplicationTransactionCommentHistoryController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentHistoryController.php', 'PhabricatorApplicationTransactionCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php', + 'PhabricatorApplicationTransactionCommentQuoteController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentQuoteController.php', 'PhabricatorApplicationTransactionCommentRemoveController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentRemoveController.php', 'PhabricatorApplicationTransactionCommentView' => 'applications/transactions/view/PhabricatorApplicationTransactionCommentView.php', 'PhabricatorApplicationTransactionController' => 'applications/transactions/controller/PhabricatorApplicationTransactionController.php', @@ -3936,6 +3937,7 @@ 'PhabricatorApplicationTransactionCommentEditor' => 'PhabricatorEditor', 'PhabricatorApplicationTransactionCommentHistoryController' => 'PhabricatorApplicationTransactionController', 'PhabricatorApplicationTransactionCommentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorApplicationTransactionCommentQuoteController' => 'PhabricatorApplicationTransactionController', 'PhabricatorApplicationTransactionCommentRemoveController' => 'PhabricatorApplicationTransactionController', 'PhabricatorApplicationTransactionCommentView' => 'AphrontView', 'PhabricatorApplicationTransactionController' => 'PhabricatorController', diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -256,6 +256,11 @@ $target, $all_changesets); + if (!$viewer_is_anonymous) { + $comment_view->setQuoteRef('D'.$revision->getID()); + $comment_view->setQuoteTargetID('comment-content'); + } + $wrap_id = celerity_generate_unique_node_id(); $comment_view = phutil_tag( 'div', @@ -373,8 +378,6 @@ $comment_form->setErrorView($review_warnings_panel); } - // TODO: Restore the ability for fields to add accept warnings. - $comment_form->setActions($this->getRevisionCommentActions($revision)); $action_uri = $this->getApplicationURI( 'comment/save/'.$revision->getID().'/'); @@ -415,6 +418,7 @@ $changeset_view, )); if ($comment_form) { + $page_pane->appendChild($comment_form); } else { // TODO: For now, just use this to get "Login to Comment". diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -377,6 +377,8 @@ ->setFlush(true) ->setHeaderText($comment_header) ->appendChild($comment_form); + $timeline->setQuoteTargetID('transaction-comments'); + $timeline->setQuoteRef($object_name); } $object_box = id(new PHUIObjectBoxView()) diff --git a/src/applications/transactions/application/PhabricatorApplicationTransactions.php b/src/applications/transactions/application/PhabricatorApplicationTransactions.php --- a/src/applications/transactions/application/PhabricatorApplicationTransactions.php +++ b/src/applications/transactions/application/PhabricatorApplicationTransactions.php @@ -19,6 +19,8 @@ => 'PhabricatorApplicationTransactionCommentRemoveController', 'history/(?[^/]+)/' => 'PhabricatorApplicationTransactionCommentHistoryController', + 'quote/(?[^/]+)/' + => 'PhabricatorApplicationTransactionCommentQuoteController', 'detail/(?[^/]+)/' => 'PhabricatorApplicationTransactionDetailController', '(?Pold|new)/(?[^/]+)/' diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentQuoteController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentQuoteController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentQuoteController.php @@ -0,0 +1,67 @@ +phid = $data['phid']; + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $xaction = id(new PhabricatorObjectQuery()) + ->withPHIDs(array($this->phid)) + ->setViewer($viewer) + ->executeOne(); + if (!$xaction) { + return new Aphront404Response(); + } + + if (!$xaction->getComment()) { + return new Aphront404Response(); + } + + if ($xaction->getComment()->getIsRemoved()) { + return new Aphront400Response(); + } + + if (!$xaction->hasComment()) { + return new Aphront404Response(); + } + + $content = $xaction->getComment()->getContent(); + $content = phutil_split_lines($content, true); + foreach ($content as $key => $line) { + if (strlen($line) && ($line[0] != '>')) { + $content[$key] = '> '.$line; + } else { + $content[$key] = '>'.$line; + } + } + $content = implode('', $content); + + $author = id(new PhabricatorHandleQuery()) + ->setViewer($viewer) + ->withPHIDs(array($xaction->getComment()->getAuthorPHID())) + ->executeOne(); + + $ref = $request->getStr('ref'); + if (strlen($ref)) { + $quote = pht('In %s, %s wrote:', $ref, '@'.$author->getName()); + } else { + $quote = pht('%s wrote:', '@'.$author->getName()); + } + + $content = ">>! {$quote}\n{$content}"; + + return id(new AphrontAjaxResponse())->setContent( + array( + 'quoteText' => $content, + )); + } + +} diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -12,6 +12,26 @@ private $isPreview; private $objectPHID; private $shouldTerminate = false; + private $quoteTargetID; + private $quoteRef; + + public function setQuoteRef($quote_ref) { + $this->quoteRef = $quote_ref; + return $this; + } + + public function getQuoteRef() { + return $this->quoteRef; + } + + public function setQuoteTargetID($quote_target_id) { + $this->quoteTargetID = $quote_target_id; + return $this; + } + + public function getQuoteTargetID() { + return $this->quoteTargetID; + } public function setObjectPHID($object_phid) { $this->objectPHID = $object_phid; @@ -376,6 +396,17 @@ $event->setIsEdited(true); } + // If we have a place for quoted text to go and this is a quotable + // comment, pass the quote target ID to the event view. + if ($this->getQuoteTargetID()) { + if ($xaction->hasComment()) { + if (!$has_removed_comment && !$has_deleted_comment) { + $event->setQuoteTargetID($this->getQuoteTargetID()); + $event->setQuoteRef($this->getQuoteRef()); + } + } + } + $can_edit = PhabricatorPolicyCapability::CAN_EDIT; if ($xaction->hasComment() || $has_deleted_comment) { diff --git a/src/view/phui/PHUITimelineEventView.php b/src/view/phui/PHUITimelineEventView.php --- a/src/view/phui/PHUITimelineEventView.php +++ b/src/view/phui/PHUITimelineEventView.php @@ -21,6 +21,26 @@ private $hideByDefault; private $token; private $tokenRemoved; + private $quoteTargetID; + private $quoteRef; + + public function setQuoteRef($quote_ref) { + $this->quoteRef = $quote_ref; + return $this; + } + + public function getQuoteRef() { + return $this->quoteRef; + } + + public function setQuoteTargetID($quote_target_id) { + $this->quoteTargetID = $quote_target_id; + return $this; + } + + public function getQuoteTargetID() { + return $this->quoteTargetID; + } public function setHideByDefault($hide_by_default) { $this->hideByDefault = $hide_by_default; @@ -357,6 +377,31 @@ } else { $xaction_phid = $this->getTransactionPHID(); + if ($this->getQuoteTargetID()) { + + $ref = null; + if ($this->getQuoteRef()) { + $ref = $this->getQuoteRef(); + if ($this->anchor) { + $ref = $ref.'#'.$this->anchor; + } + } + + $extra[] = javelin_tag( + 'a', + array( + 'href' => '#', + 'sigil' => 'transaction-quote', + 'mustcapture' => true, + 'meta' => array( + 'targetID' => $this->getQuoteTargetID(), + 'uri' => '/transactions/quote/'.$xaction_phid.'/', + 'ref' => $ref, + ), + ), + pht('Quote')); + } + if ($this->getIsEdited()) { $extra[] = javelin_tag( 'a', diff --git a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js --- a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js +++ b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js @@ -6,6 +6,7 @@ * javelin-dom * javelin-fx * javelin-util + * phabricator-textareautils */ JX.behavior('phabricator-transaction-list', function(config) { @@ -115,6 +116,37 @@ e.kill(); }); + JX.DOM.listen( + list, + 'click', + 'transaction-quote', + function(e) { + e.kill(); + + var data = e.getNodeData('transaction-quote'); + new JX.Workflow(data.uri) + .setData({ref: data.ref}) + .setHandler(function(r) { + var textarea = JX.$(data.targetID); + + JX.DOM.scrollTo(textarea); + + var value = textarea.value; + if (value.length) { + value += "\n\n"; + } + value += r.quoteText; + value += "\n\n"; + textarea.value = value; + + JX.TextAreaUtils.setSelectionRange( + textarea, + textarea.value.length, + textarea.value.length); + }) + .start(); + }); + JX.Stratcom.listen( ['submit', 'didSyntheticSubmit'], 'transaction-append',