diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php @@ -3,11 +3,16 @@ final class PhabricatorApplicationTransactionDetailController extends PhabricatorApplicationTransactionController { + private $objectHandle; + public function shouldAllowPublic() { return true; } public function handleRequest(AphrontRequest $request) { + // Users can end up on this page directly by following links in email, + // so we try to make it somewhat reasonable as a standalone page. + $viewer = $this->getViewer(); $phid = $request->getURIData('phid'); @@ -20,13 +25,39 @@ } $details = $xaction->renderChangeDetails($viewer); - $cancel_uri = $this->guessCancelURI($viewer, $xaction); + + $object_phid = $xaction->getObjectPHID(); + $handles = $viewer->loadHandles(array($object_phid)); + $handle = $handles[$object_phid]; + $this->objectHandle = $handle; + + $cancel_uri = $handle->getURI(); + + if ($request->isAjax()) { + $button_text = pht('Done'); + } else { + $button_text = pht('Continue'); + } return $this->newDialog() ->setTitle(pht('Change Details')) ->setWidth(AphrontDialogView::WIDTH_FORM) ->appendChild($details) - ->addCancelButton($cancel_uri); + ->addCancelButton($cancel_uri, $button_text); } + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $handle = $this->objectHandle; + if ($handle) { + $crumbs->addTextCrumb( + $handle->getObjectName(), + $handle->getURI()); + } + + return $crumbs; + } + + } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2704,7 +2704,9 @@ $object_href = null) { $headers = array(); + $headers_html = array(); $comments = array(); + $details = array(); foreach ($xactions as $xaction) { if ($xaction->shouldHideForMail($xactions)) { @@ -2716,16 +2718,25 @@ $headers[] = $header; } + $header_html = $xaction->getTitleForHTMLMail(); + if ($header_html !== null) { + $headers_html[] = $header_html; + } + $comment = $xaction->getBodyForMail(); if ($comment !== null) { $comments[] = $comment; } + + if ($xaction->hasChangeDetailsForMail()) { + $details[] = $xaction; + } } $headers_text = implode("\n", $headers); $body->addRawPlaintextSection($headers_text); - $headers_html = phutil_implode_html(phutil_tag('br'), $headers); + $headers_html = phutil_implode_html(phutil_tag('br'), $headers_html); $header_button = null; if ($object_label !== null) { @@ -2765,7 +2776,13 @@ array( 'style' => implode(' ', $xactions_style), ), - $headers_html); + array( + $headers_html, + // Add an extra newline to prevent the "View Object" button from + // running into the transaction text in Mail.app text snippet + // previews. + "\n", + )); $headers_html = phutil_tag( 'table', @@ -2777,6 +2794,14 @@ foreach ($comments as $comment) { $body->addRemarkupSection(null, $comment); } + + foreach ($details as $xaction) { + $details = $xaction->renderChangeDetailsForMail($body->getViewer()); + if ($details !== null) { + $body->addHTMLSection(pht('EDIT DETAILS'), $details); + } + } + } /** diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -693,6 +693,33 @@ return id(clone $this)->setRenderingTarget('text')->getTitle(); } + public function getTitleForHTMLMail() { + $title = $this->getTitleForMail(); + if ($title === null) { + return null; + } + + if ($this->hasChangeDetails()) { + $details_uri = $this->getChangeDetailsURI(); + $details_uri = PhabricatorEnv::getProductionURI($details_uri); + + $show_details = phutil_tag( + 'a', + array( + 'href' => $details_uri, + ), + pht('(Show Details)')); + + $title = array($title, ' ', $show_details); + } + + return $title; + } + + public function getChangeDetailsURI() { + return '/transactions/detail/'.$this->getPHID().'/'; + } + public function getBodyForMail() { if ($this->isInlineCommentTransaction()) { // We don't return inline comment content as mail body content, because @@ -1307,6 +1334,18 @@ return false; } + public function hasChangeDetailsForMail() { + return $this->hasChangeDetails(); + } + + public function renderChangeDetailsForMail(PhabricatorUser $viewer) { + $view = $this->renderChangeDetails($viewer); + if ($view instanceof PhabricatorApplicationTransactionTextDiffDetailView) { + return $view->renderForMail(); + } + return null; + } + public function renderChangeDetails(PhabricatorUser $viewer) { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_CUSTOMFIELD: @@ -1327,15 +1366,10 @@ PhabricatorUser $viewer, $old, $new) { - - require_celerity_resource('differential-changeset-view-css'); - - $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) ->setUser($viewer) ->setOldText($old) ->setNewText($new); - - return $view->render(); } public function attachTransactionGroup(array $group) { diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php --- a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php @@ -16,6 +16,62 @@ return $this; } + public function renderForMail() { + $diff = $this->buildDiff(); + + $old_styles = array( + 'padding: 0 2px;', + 'color: #802b2b;', + 'background: rgba(251, 175, 175, .7);', + ); + $old_styles = implode(' ', $old_styles); + + $new_styles = array( + 'padding: 0 2px;', + 'color: #3e6d35;', + 'background: rgba(151, 234, 151, .6);', + ); + $new_styles = implode(' ', $new_styles); + + $result = array(); + foreach ($diff->getParts() as $part) { + $type = $part['type']; + $text = $part['text']; + switch ($type) { + case '-': + $result[] = phutil_tag( + 'span', + array( + 'style' => $old_styles, + ), + $text); + break; + case '+': + $result[] = phutil_tag( + 'span', + array( + 'style' => $new_styles, + ), + $text); + break; + case '=': + $result[] = $text; + break; + } + } + + $styles = array( + 'white-space: pre-wrap;', + ); + + return phutil_tag( + 'div', + array( + 'style' => implode(' ', $styles), + ), + $result); + } + public function render() { $diff = $this->buildDiff(); 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 @@ -259,7 +259,7 @@ return javelin_tag( 'a', array( - 'href' => '/transactions/detail/'.$xaction->getPHID().'/', + 'href' => $xaction->getChangeDetailsURI(), 'sigil' => 'workflow', ), pht('(Show Details)'));