1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-19 05:12:41 +01:00

Improve HTML mail rendering of inline patches

Summary: Fixes T9790. This uses a simple renderer, like the inline context renderer, that emphasizes getting a quick glance at small changes and working reasonably on mobile devices.

Test Plan:
  - Set `inline` setting to `9999`.
  - Created a diff.
  - Saw it render reasonably in HTML mail.
  - Also tested text mail to make sure I didn't break that.

{F1310137, size=full}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9790

Differential Revision: https://secure.phabricator.com/D15901
This commit is contained in:
epriestley 2016-05-12 09:59:07 -07:00
parent 9d196648f5
commit bd9bcaa8ff
8 changed files with 228 additions and 79 deletions

View file

@ -362,6 +362,7 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php', 'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php',
'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php',
'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php',
'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php', 'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php',
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php', 'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php', 'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php',
@ -471,6 +472,7 @@ phutil_register_library_map(array(
'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php', 'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php',
'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php', 'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php',
'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php', 'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php',
'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php',
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php', 'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php', 'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php',
'DifferentialNextStepField' => 'applications/differential/customfield/DifferentialNextStepField.php', 'DifferentialNextStepField' => 'applications/differential/customfield/DifferentialNextStepField.php',
@ -4551,6 +4553,7 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField', 'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField',
'DifferentialBlockHeraldAction' => 'HeraldAction', 'DifferentialBlockHeraldAction' => 'HeraldAction',
'DifferentialBranchField' => 'DifferentialCustomField', 'DifferentialBranchField' => 'DifferentialCustomField',
'DifferentialChangeDetailMailView' => 'DifferentialMailView',
'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup', 'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup',
'DifferentialChangeType' => 'Phobject', 'DifferentialChangeType' => 'Phobject',
'DifferentialChangesSinceLastUpdateField' => 'DifferentialCustomField', 'DifferentialChangesSinceLastUpdateField' => 'DifferentialCustomField',
@ -4665,7 +4668,7 @@ phutil_register_library_map(array(
'PhabricatorInlineCommentInterface', 'PhabricatorInlineCommentInterface',
), ),
'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController', 'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
'DifferentialInlineCommentMailView' => 'Phobject', 'DifferentialInlineCommentMailView' => 'DifferentialMailView',
'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', 'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery', 'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField', 'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
@ -4676,6 +4679,7 @@ phutil_register_library_map(array(
'DifferentialLintField' => 'DifferentialHarbormasterField', 'DifferentialLintField' => 'DifferentialHarbormasterField',
'DifferentialLintStatus' => 'Phobject', 'DifferentialLintStatus' => 'Phobject',
'DifferentialLocalCommitsView' => 'AphrontView', 'DifferentialLocalCommitsView' => 'AphrontView',
'DifferentialMailView' => 'Phobject',
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
'DifferentialModernHunk' => 'DifferentialHunk', 'DifferentialModernHunk' => 'DifferentialHunk',
'DifferentialNextStepField' => 'DifferentialCustomField', 'DifferentialNextStepField' => 'DifferentialCustomField',

View file

@ -1251,21 +1251,18 @@ final class DifferentialTransactionEditor
$config_attach = PhabricatorEnv::getEnvConfig($config_key_attach); $config_attach = PhabricatorEnv::getEnvConfig($config_key_attach);
if ($config_inline || $config_attach) { if ($config_inline || $config_attach) {
$patch_section = $this->renderPatchForMail($diff); $patch = $this->buildPatchForMail($diff);
$lines = count(phutil_split_lines($patch_section->getPlaintext())); $lines = substr_count($patch, "\n");
if ($config_inline && ($lines <= $config_inline)) { if ($config_inline && ($lines <= $config_inline)) {
$body->addTextSection( $this->appendChangeDetailsForMail($object, $diff, $patch, $body);
pht('CHANGE DETAILS'),
$patch_section);
} }
if ($config_attach) { if ($config_attach) {
$name = pht('D%s.%s.patch', $object->getID(), $diff->getID()); $name = pht('D%s.%s.patch', $object->getID(), $diff->getID());
$mime_type = 'text/x-patch; charset=utf-8'; $mime_type = 'text/x-patch; charset=utf-8';
$body->addAttachment( $body->addAttachment(
new PhabricatorMetaMTAAttachment( new PhabricatorMetaMTAAttachment($patch, $name, $mime_type));
$patch_section->getPlaintext(), $name, $mime_type));
} }
} }
} }
@ -1387,7 +1384,38 @@ final class DifferentialTransactionEditor
$section_text = "\n".$section->getPlaintext(); $section_text = "\n".$section->getPlaintext();
$style = array( $style = array(
'margin: 12px 0;', 'margin: 6px 0 12px 0;',
);
$section_html = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$section->getHTML());
$body->addPlaintextSection($header, $section_text, false);
$body->addHTMLSection($header, $section_html);
}
private function appendChangeDetailsForMail(
PhabricatorLiskDAO $object,
DifferentialDiff $diff,
$patch,
PhabricatorMetaMTAMailBody $body) {
$section = id(new DifferentialChangeDetailMailView())
->setViewer($this->getActor())
->setDiff($diff)
->setPatch($patch)
->buildMailSection();
$header = pht('CHANGE DETAILS');
$section_text = "\n".$section->getPlaintext();
$style = array(
'margin: 6px 0 12px 0;',
); );
$section_html = phutil_tag( $section_html = phutil_tag(
@ -1659,20 +1687,14 @@ final class DifferentialTransactionEditor
array('style' => 'font-family: monospace;'), $patch); array('style' => 'font-family: monospace;'), $patch);
} }
private function renderPatchForMail(DifferentialDiff $diff) { private function buildPatchForMail(DifferentialDiff $diff) {
$format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format');
$patch = id(new DifferentialRawDiffRenderer()) return id(new DifferentialRawDiffRenderer())
->setViewer($this->getActor()) ->setViewer($this->getActor())
->setFormat($format) ->setFormat($format)
->setChangesets($diff->getChangesets()) ->setChangesets($diff->getChangesets())
->buildPatch(); ->buildPatch();
$section = new PhabricatorMetaMTAMailSection();
$section->addHTMLFragment($this->renderPatchHTMLForMail($patch));
$section->addPlaintextFragment($patch);
return $section;
} }
protected function willPublish(PhabricatorLiskDAO $object, array $xactions) { protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {

View file

@ -0,0 +1,77 @@
<?php
final class DifferentialChangeDetailMailView
extends DifferentialMailView {
private $viewer;
private $diff;
private $patch;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function getDiff() {
return $this->diff;
}
public function setPatch($patch) {
$this->patch = $patch;
return $this;
}
public function getPatch() {
return $this->patch;
}
public function buildMailSection() {
$viewer = $this->getViewer();
$diff = $this->getDiff();
$engine = new PhabricatorMarkupEngine();
$out = array();
foreach ($diff->getChangesets() as $changeset) {
$parser = id(new DifferentialChangesetParser())
->setUser($viewer)
->setChangeset($changeset)
->setLinesOfContext(2)
->setMarkupEngine($engine);
$parser->setRenderer(new DifferentialChangesetOneUpMailRenderer());
$block = $parser->render();
$filename = $changeset->getFilename();
$filename = $this->renderHeaderBold($filename);
$header = $this->renderHeaderBlock($filename);
$out[] = $this->renderContentBox(
array(
$header,
$this->renderCodeBlock($block),
));
}
$out = phutil_implode_html(phutil_tag('br'), $out);
$patch_html = $out;
$patch_text = $this->getPatch();
return id(new PhabricatorMetaMTAMailSection())
->addPlaintextFragment($patch_text)
->addHTMLFragment($patch_html);
}
}

View file

@ -1,7 +1,7 @@
<?php <?php
final class DifferentialInlineCommentMailView final class DifferentialInlineCommentMailView
extends Phobject { extends DifferentialMailView {
private $viewer; private $viewer;
private $inlines; private $inlines;
@ -85,16 +85,7 @@ final class DifferentialInlineCommentMailView
$section->addPlaintextFragment($spacer_text); $section->addPlaintextFragment($spacer_text);
$section->addPlaintextFragment($render_text); $section->addPlaintextFragment($render_text);
$style = array( $html_fragment = $this->renderContentBox(
'border: 1px solid #C7CCD9;',
'border-radius: 3px;',
);
$html_fragment = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
array( array(
$context_html, $context_html,
$render_html, $render_html,
@ -374,21 +365,7 @@ final class DifferentialInlineCommentMailView
$is_html) { $is_html) {
if ($is_html) { if ($is_html) {
$style = array( $patch = $this->renderCodeBlock($patch);
'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;',
'white-space: pre-wrap;',
'clear: both;',
'padding: 4px 0;',
'margin: 0;',
);
$style = implode(' ', $style);
$patch = phutil_tag(
'div',
array(
'style' => $style,
),
$patch);
} }
$header = $this->renderHeader($comment, $is_html, false); $header = $this->renderHeader($comment, $is_html, false);
@ -430,12 +407,7 @@ final class DifferentialInlineCommentMailView
$header = "{$path}:{$range}"; $header = "{$path}:{$range}";
if ($is_html) { if ($is_html) {
$header = phutil_tag( $header = $this->renderHeaderBold($header);
'span',
array(
'style' => 'color: #4b4d51; font-weight: bold;',
),
$header);
} }
if ($with_author) { if ($with_author) {
@ -448,12 +420,7 @@ final class DifferentialInlineCommentMailView
$byline = $author->getName(); $byline = $author->getName();
if ($is_html) { if ($is_html) {
$byline = phutil_tag( $byline = $this->renderHeaderBold($byline);
'span',
array(
'style' => 'color: #4b4d51; font-weight: bold;',
),
$byline);
} }
$header = pht('%s wrote in %s', $byline, $header); $header = pht('%s wrote in %s', $byline, $header);
@ -478,22 +445,7 @@ final class DifferentialInlineCommentMailView
$link = null; $link = null;
} }
$style = array( $header = $this->renderHeaderBlock(array($link, $header));
'color: #74777d;',
'background: #eff2f4;',
'padding: 6px 8px;',
'overflow: hidden;',
);
$header = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
array(
$link,
$header,
));
} }
return $header; return $header;

View file

@ -0,0 +1,62 @@
<?php
abstract class DifferentialMailView
extends Phobject {
protected function renderCodeBlock($block) {
$style = array(
'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;',
'white-space: pre-wrap;',
'clear: both;',
'padding: 4px 0;',
'margin: 0;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$block);
}
protected function renderHeaderBlock($block) {
$style = array(
'color: #74777d;',
'background: #eff2f4;',
'padding: 6px 8px;',
'overflow: hidden;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$block);
}
protected function renderHeaderBold($content) {
return phutil_tag(
'span',
array(
'style' => 'color: #4b4d51; font-weight: bold;',
),
$content);
}
protected function renderContentBox($content) {
$style = array(
'border: 1px solid #C7CCD9;',
'border-radius: 3px;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$content);
}
}

View file

@ -55,6 +55,7 @@ final class DifferentialChangesetParser extends Phobject {
private $rangeStart; private $rangeStart;
private $rangeEnd; private $rangeEnd;
private $mask; private $mask;
private $linesOfContext = 8;
private $highlightEngine; private $highlightEngine;
@ -195,8 +196,6 @@ final class DifferentialChangesetParser extends Phobject {
const ATTR_WHITELINES = 'attr:white'; const ATTR_WHITELINES = 'attr:white';
const ATTR_MOVEAWAY = 'attr:moveaway'; const ATTR_MOVEAWAY = 'attr:moveaway';
const LINES_CONTEXT = 8;
const WHITESPACE_SHOW_ALL = 'show-all'; const WHITESPACE_SHOW_ALL = 'show-all';
const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing'; const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing';
const WHITESPACE_IGNORE_MOST = 'ignore-most'; const WHITESPACE_IGNORE_MOST = 'ignore-most';
@ -227,6 +226,16 @@ final class DifferentialChangesetParser extends Phobject {
return $this; return $this;
} }
public function setLinesOfContext($lines_of_context) {
$this->linesOfContext = $lines_of_context;
return $this;
}
public function getLinesOfContext() {
return $this->linesOfContext;
}
/** /**
* Configure which Changeset comments added to the right side of the visible * Configure which Changeset comments added to the right side of the visible
* diff will be attached to. The ID must be the ID of a real Differential * diff will be attached to. The ID must be the ID of a real Differential
@ -724,8 +733,10 @@ final class DifferentialChangesetParser extends Phobject {
self::ATTR_MOVEAWAY => $moveaway, self::ATTR_MOVEAWAY => $moveaway,
)); ));
$lines_context = $this->getLinesOfContext();
$hunk_parser->generateIntraLineDiffs(); $hunk_parser->generateIntraLineDiffs();
$hunk_parser->generateVisibileLinesMask(); $hunk_parser->generateVisibileLinesMask($lines_context);
$this->setOldLines($hunk_parser->getOldLines()); $this->setOldLines($hunk_parser->getOldLines());
$this->setNewLines($hunk_parser->getNewLines()); $this->setNewLines($hunk_parser->getNewLines());
@ -959,6 +970,7 @@ final class DifferentialChangesetParser extends Phobject {
$old_mask = array(); $old_mask = array();
$new_mask = array(); $new_mask = array();
$feedback_mask = array(); $feedback_mask = array();
$lines_context = $this->getLinesOfContext();
if ($this->comments) { if ($this->comments) {
// If there are any comments which appear in sections of the file which // If there are any comments which appear in sections of the file which
@ -1001,10 +1013,10 @@ final class DifferentialChangesetParser extends Phobject {
} }
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0); $start = max($comment->getLineNumber() - $lines_context, 0);
$end = $comment->getLineNumber() + $end = $comment->getLineNumber() +
$comment->getLineLength() + $comment->getLineLength() +
self::LINES_CONTEXT; $lines_context;
for ($ii = $start; $ii <= $end; $ii++) { for ($ii = $start; $ii <= $end; $ii++) {
if ($new_side) { if ($new_side) {
$new_mask[$ii] = true; $new_mask[$ii] = true;
@ -1189,6 +1201,8 @@ final class DifferentialChangesetParser extends Phobject {
$range_start, $range_start,
$range_len) { $range_len) {
$lines_context = $this->getLinesOfContext();
// Calculate gaps and mask first // Calculate gaps and mask first
$gaps = array(); $gaps = array();
$gap_start = 0; $gap_start = 0;
@ -1199,7 +1213,7 @@ final class DifferentialChangesetParser extends Phobject {
if (isset($base_mask[$ii])) { if (isset($base_mask[$ii])) {
if ($in_gap) { if ($in_gap) {
$gap_length = $ii - $gap_start; $gap_length = $ii - $gap_start;
if ($gap_length <= self::LINES_CONTEXT) { if ($gap_length <= $lines_context) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) { for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$base_mask[$jj] = true; $base_mask[$jj] = true;
} }

View file

@ -353,8 +353,7 @@ final class DifferentialHunkParser extends Phobject {
return $this; return $this;
} }
public function generateVisibileLinesMask() { public function generateVisibileLinesMask($lines_context) {
$lines_context = DifferentialChangesetParser::LINES_CONTEXT;
$old = $this->getOldLines(); $old = $this->getOldLines();
$new = $this->getNewLines(); $new = $this->getNewLines();
$max_length = max(count($old), count($new)); $max_length = max(count($old), count($new));

View file

@ -51,6 +51,16 @@ final class DifferentialChangesetOneUpMailRenderer
protected function renderPrimitives(array $primitives, $rows) { protected function renderPrimitives(array $primitives, $rows) {
$out = array(); $out = array();
$context_style = array(
'background: #F7F7F7;',
'color: #74777D;',
'border-style: dashed;',
'border-color: #C7CCD9;',
'border-width: 1px 0;',
);
$context_style = implode(' ', $context_style);
foreach ($primitives as $k => $p) { foreach ($primitives as $k => $p) {
$type = $p['type']; $type = $p['type'];
switch ($type) { switch ($type) {
@ -80,6 +90,15 @@ final class DifferentialChangesetOneUpMailRenderer
'text' => (string)$p['render'], 'text' => (string)$p['render'],
); );
break; break;
case 'context':
// NOTE: These are being included with no text so they get stripped
// in the header and footer.
$out[] = array(
'style' => $context_style,
'render' => '...',
'text' => '',
);
break;
default: default:
break; break;
} }