Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15446586
D15850.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D15850.diff
View Options
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
@@ -459,6 +459,7 @@
'DifferentialHunkTestCase' => 'applications/differential/storage/__tests__/DifferentialHunkTestCase.php',
'DifferentialInlineComment' => 'applications/differential/storage/DifferentialInlineComment.php',
'DifferentialInlineCommentEditController' => 'applications/differential/controller/DifferentialInlineCommentEditController.php',
+ 'DifferentialInlineCommentMailView' => 'applications/differential/mail/DifferentialInlineCommentMailView.php',
'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/DifferentialInlineCommentPreviewController.php',
'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php',
@@ -4660,6 +4661,7 @@
'PhabricatorInlineCommentInterface',
),
'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
+ 'DifferentialInlineCommentMailView' => 'Phobject',
'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php
--- a/src/applications/differential/editor/DifferentialTransactionEditor.php
+++ b/src/applications/differential/editor/DifferentialTransactionEditor.php
@@ -1374,138 +1374,13 @@
return $result;
}
- protected function indentForMail(array $lines) {
- $indented = array();
- foreach ($lines as $line) {
- $indented[] = '> '.$line;
- }
- return $indented;
- }
-
- protected function nestCommentHistory(
- DifferentialTransactionComment $comment, array $comments_by_line_number,
- array $users_by_phid) {
-
- $nested = array();
- $previous_comments = $comments_by_line_number[$comment->getChangesetID()]
- [$comment->getLineNumber()];
- foreach ($previous_comments as $previous_comment) {
- if ($previous_comment->getID() >= $comment->getID()) {
- break;
- }
- $nested = $this->indentForMail(
- array_merge(
- $nested,
- explode("\n", $previous_comment->getContent())));
- $user = idx($users_by_phid, $previous_comment->getAuthorPHID(), null);
- if ($user) {
- array_unshift($nested, pht('%s wrote:', $user->getUserName()));
- }
- }
-
- $nested = array_merge($nested, explode("\n", $comment->getContent()));
- return implode("\n", $nested);
- }
-
private function renderInlineCommentsForMail(
PhabricatorLiskDAO $object,
array $inlines) {
-
- $context_key = 'metamta.differential.unified-comment-context';
- $show_context = PhabricatorEnv::getEnvConfig($context_key);
-
- $changeset_ids = array();
- $line_numbers_by_changeset = array();
- foreach ($inlines as $inline) {
- $id = $inline->getComment()->getChangesetID();
- $changeset_ids[$id] = $id;
- $line_numbers_by_changeset[$id][] =
- $inline->getComment()->getLineNumber();
- }
-
- $changesets = id(new DifferentialChangesetQuery())
+ return id(new DifferentialInlineCommentMailView())
->setViewer($this->getActor())
- ->withIDs($changeset_ids)
- ->needHunks(true)
- ->execute();
-
- $inline_groups = DifferentialTransactionComment::sortAndGroupInlines(
- $inlines,
- $changesets);
-
- if ($show_context) {
- $hunk_parser = new DifferentialHunkParser();
- $table = new DifferentialTransactionComment();
- $conn_r = $table->establishConnection('r');
- $queries = array();
- foreach ($line_numbers_by_changeset as $id => $line_numbers) {
- $queries[] = qsprintf(
- $conn_r,
- '(changesetID = %d AND lineNumber IN (%Ld))',
- $id, $line_numbers);
- }
- $all_comments = id(new DifferentialTransactionComment())->loadAllWhere(
- 'transactionPHID IS NOT NULL AND (%Q)', implode(' OR ', $queries));
- $comments_by_line_number = array();
- foreach ($all_comments as $comment) {
- $comments_by_line_number
- [$comment->getChangesetID()]
- [$comment->getLineNumber()]
- [$comment->getID()] = $comment;
- }
- $author_phids = mpull($all_comments, 'getAuthorPHID');
- $authors = id(new PhabricatorPeopleQuery())
- ->setViewer($this->getActor())
- ->withPHIDs($author_phids)
- ->execute();
- $authors_by_phid = mpull($authors, null, 'getPHID');
- }
-
- $section = new PhabricatorMetaMTAMailSection();
- foreach ($inline_groups as $changeset_id => $group) {
- $changeset = idx($changesets, $changeset_id);
- if (!$changeset) {
- continue;
- }
-
- foreach ($group as $inline) {
- $comment = $inline->getComment();
- $file = $changeset->getFilename();
- $start = $comment->getLineNumber();
- $len = $comment->getLineLength();
- if ($len) {
- $range = $start.'-'.($start + $len);
- } else {
- $range = $start;
- }
-
- $inline_content = $comment->getContent();
-
- if (!$show_context) {
- $section->addFragment("{$file}:{$range} {$inline_content}");
- } else {
- $patch = $hunk_parser->makeContextDiff(
- $changeset->getHunks(),
- $comment->getIsNewFile(),
- $comment->getLineNumber(),
- $comment->getLineLength(),
- 1);
- $nested_comments = $this->nestCommentHistory(
- $inline->getComment(), $comments_by_line_number, $authors_by_phid);
-
- $section
- ->addFragment('================')
- ->addFragment(pht('Comment at: %s:%s', $file, $range))
- ->addPlaintextFragment($patch)
- ->addHTMLFragment($this->renderPatchHTMLForMail($patch))
- ->addFragment('----------------')
- ->addFragment($nested_comments)
- ->addFragment(null);
- }
- }
- }
-
- return $section;
+ ->setInlines($inlines)
+ ->buildMailSection();
}
private function loadDiff($phid, $need_changesets = false) {
diff --git a/src/applications/differential/mail/DifferentialInlineCommentMailView.php b/src/applications/differential/mail/DifferentialInlineCommentMailView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/mail/DifferentialInlineCommentMailView.php
@@ -0,0 +1,382 @@
+<?php
+
+final class DifferentialInlineCommentMailView
+ extends Phobject {
+
+ private $viewer;
+ private $inlines;
+ private $changesets;
+ private $authors;
+
+ public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
+ public function setInlines($inlines) {
+ $this->inlines = $inlines;
+ return $this;
+ }
+
+ public function getInlines() {
+ return $this->inlines;
+ }
+
+ public function buildMailSection() {
+ $inlines = $this->getInlines();
+
+ $comments = mpull($inlines, 'getComment');
+ $comments = mpull($comments, null, 'getPHID');
+ $parents = $this->loadParents($comments);
+ $all_comments = $comments + $parents;
+
+ $this->changesets = $this->loadChangesets($all_comments);
+ $this->authors = $this->loadAuthors($all_comments);
+ $groups = $this->groupInlines($inlines);
+
+ $hunk_parser = new DifferentialHunkParser();
+
+ $spacer_text = null;
+ $spacer_html = phutil_tag('br');
+
+ $section = new PhabricatorMetaMTAMailSection();
+
+ $last_group_key = last_key($groups);
+ foreach ($groups as $changeset_id => $group) {
+ $changeset = $this->getChangeset($changeset_id);
+ if (!$changeset) {
+ continue;
+ }
+
+ $is_last_group = ($changeset_id == $last_group_key);
+
+ $last_inline_key = last_key($group);
+ foreach ($group as $inline_key => $inline) {
+ $comment = $inline->getComment();
+ $parent_phid = $comment->getReplyToCommentPHID();
+
+ $is_last_inline = ($inline_key == $last_inline_key);
+
+ $context_text = null;
+ $context_html = null;
+
+ if ($parent_phid) {
+ $parent = idx($parents, $parent_phid);
+ if ($parent) {
+ $context_text = $this->renderInline($parent, false, true);
+ $context_html = $this->renderInline($parent, true, true);
+ }
+ } else {
+ $patch = $this->getPatch($hunk_parser, $comment);
+ $context_text = $this->renderPatch($comment, $patch, false);
+ $context_html = $this->renderPatch($comment, $patch, true);
+ }
+
+ $render_text = $this->renderInline($comment, false, false);
+ $render_html = $this->renderInline($comment, true, false);
+
+ $section->addPlaintextFragment($context_text);
+ $section->addHTMLFragment($context_html);
+
+ $section->addPlaintextFragment($spacer_text);
+ $section->addHTMLFragment($spacer_html);
+
+ $section->addPlaintextFragment($render_text);
+ $section->addHTMLFragment($render_html);
+
+ if (!$is_last_group || !$is_last_inline) {
+ $section->addPlaintextFragment($spacer_text);
+ $section->addHTMLFragment($spacer_html);
+ }
+ }
+ }
+
+ return $section;
+ }
+
+ private function loadChangesets(array $comments) {
+ if (!$comments) {
+ return array();
+ }
+
+ $ids = array();
+ foreach ($comments as $comment) {
+ $ids[] = $comment->getChangesetID();
+ }
+
+ $changesets = id(new DifferentialChangesetQuery())
+ ->setViewer($this->getViewer())
+ ->withIDs($ids)
+ ->needHunks(true)
+ ->execute();
+
+ return mpull($changesets, null, 'getID');
+ }
+
+ private function loadParents(array $comments) {
+ $viewer = $this->getViewer();
+
+ $phids = array();
+ foreach ($comments as $comment) {
+ $parent_phid = $comment->getReplyToCommentPHID();
+ if (!$parent_phid) {
+ continue;
+ }
+ $phids[] = $parent_phid;
+ }
+
+ if (!$phids) {
+ return array();
+ }
+
+ $parents = id(new DifferentialDiffInlineCommentQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($phids)
+ ->execute();
+
+ return mpull($parents, null, 'getPHID');
+ }
+
+ private function loadAuthors(array $comments) {
+ $viewer = $this->getViewer();
+
+ $phids = array();
+ foreach ($comments as $comment) {
+ $author_phid = $comment->getAuthorPHID();
+ if (!$author_phid) {
+ continue;
+ }
+ $phids[] = $author_phid;
+ }
+
+ if (!$phids) {
+ return array();
+ }
+
+ return $viewer->loadHandles($phids);
+ }
+
+ private function groupInlines(array $inlines) {
+ return DifferentialTransactionComment::sortAndGroupInlines(
+ $inlines,
+ $this->changesets);
+ }
+
+ private function renderInline(
+ DifferentialTransactionComment $comment,
+ $is_html,
+ $is_quote) {
+
+ $changeset = $this->getChangeset($comment->getChangesetID());
+ if (!$changeset) {
+ return null;
+ }
+
+ $content = $comment->getContent();
+ $content = $this->renderRemarkupContent($content, $is_html);
+
+ if ($is_quote) {
+ $header = $this->renderHeader($comment, $is_html, true);
+ } else {
+ $header = null;
+ }
+
+ $parts = array(
+ $header,
+ "\n",
+ $content,
+ );
+
+ if (!$is_html) {
+ $parts = implode('', $parts);
+ $parts = trim($parts);
+ }
+
+ if ($is_quote) {
+ if ($is_html) {
+ $parts = $this->quoteHTML($parts);
+ } else {
+ $parts = $this->quoteText($parts);
+ }
+ }
+
+ return $parts;
+ }
+
+ private function renderRemarkupContent($content, $is_html) {
+ $viewer = $this->getViewer();
+ $production_uri = PhabricatorEnv::getProductionURI('/');
+
+ if ($is_html) {
+ $mode = PhutilRemarkupEngine::MODE_HTML_MAIL;
+ } else {
+ $mode = PhutilRemarkupEngine::MODE_TEXT;
+ }
+
+ $engine = PhabricatorMarkupEngine::newMarkupEngine(array())
+ ->setConfig('viewer', $viewer)
+ ->setConfig('uri.base', $production_uri)
+ ->setMode($mode);
+
+ try {
+ return $engine->markupText($content);
+ } catch (Exception $ex) {
+ return $content;
+ }
+ }
+
+ private function getChangeset($id) {
+ return idx($this->changesets, $id);
+ }
+
+ private function getAuthor($phid) {
+ if (isset($this->authors[$phid])) {
+ return $this->authors[$phid];
+ }
+ return null;
+ }
+
+ private function quoteText($block) {
+ $block = phutil_split_lines($block);
+ foreach ($block as $key => $line) {
+ $block[$key] = '> '.$line;
+ }
+
+ return implode('', $block);
+ }
+
+ private function quoteHTML($block) {
+ $styles = array(
+ 'padding: 4px 8px;',
+ 'background: #F8F9FC;',
+ 'border-left: 3px solid #a7b5bf;',
+ );
+
+ $styles = implode(' ', $styles);
+
+ return phutil_tag(
+ 'div',
+ array(
+ 'style' => $styles,
+ ),
+ $block);
+ }
+
+ private function getPatch(
+ DifferentialHunkParser $parser,
+ DifferentialTransactionComment $comment) {
+
+ $changeset = $this->getChangeset($comment->getChangesetID());
+ $hunks = $changeset->getHunks();
+
+ $is_new = $comment->getIsNewFile();
+ $start = $comment->getLineNumber();
+ $length = $comment->getLineLength();
+
+ $diff = $parser->makeContextDiff($hunks, $is_new, $start, $length, 1);
+
+ return $diff;
+ }
+
+ private function renderPatch(
+ DifferentialTransactionComment $comment,
+ $patch,
+ $is_html) {
+
+ $patch = phutil_split_lines($patch);
+
+ // Remove the "@@ -x,y +u,v @@" line.
+ array_shift($patch);
+
+ $patch = implode('', $patch);
+
+ if ($is_html) {
+ $style = array(
+ 'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;',
+ 'padding: 0',
+ 'margin: 0;',
+ );
+
+ $style = implode(' ', $style);
+ $patch = phutil_tag(
+ 'pre',
+ array(
+ 'style' => $style,
+ ),
+ $patch);
+ }
+
+ $header = $this->renderHeader($comment, $is_html, false);
+
+ $patch = array(
+ $header,
+ "\n",
+ $patch,
+ );
+
+ if (!$is_html) {
+ $patch = implode('', $patch);
+ $patch = $this->quoteText($patch);
+ } else {
+ $patch = $this->quoteHTML($patch);
+ }
+
+ return $patch;
+ }
+
+ private function renderHeader(
+ DifferentialTransactionComment $comment,
+ $is_html,
+ $with_author) {
+
+ $changeset = $this->getChangeset($comment->getChangesetID());
+ $path = $changeset->getFilename();
+
+ $start = $comment->getLineNumber();
+ $length = $comment->getLineLength();
+ if ($length) {
+ $range = pht('%s-%s', $start, $start + $length);
+ } else {
+ $range = $start;
+ }
+
+ $header = "{$path}:{$range}";
+ if ($is_html) {
+ $header = phutil_tag('strong', array(), $header);
+ }
+
+ if ($with_author) {
+ $author = $this->getAuthor($comment->getAuthorPHID());
+ } else {
+ $author = null;
+ }
+
+ if ($author) {
+ $byline = '@'.$author->getName();
+
+ if ($is_html) {
+ $byline = phutil_tag('strong', array(), $byline);
+ }
+
+ $header = pht('%s wrote in %s', $byline, $header);
+ } else {
+ $header = pht('In %s', $header);
+ }
+
+ if ($is_html) {
+ $header = phutil_tag(
+ 'div',
+ array(
+ 'style' => 'font-style: italic',
+ ),
+ $header);
+ }
+
+ return $header;
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 28, 7:36 PM (1 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7390710
Default Alt Text
D15850.diff (15 KB)
Attached To
Mode
D15850: Rough cut of inline comment context
Attached
Detach File
Event Timeline
Log In to Comment