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

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

+228 -79
+5 -1
src/__phutil_library_map__.php
··· 362 362 'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php', 363 363 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 364 364 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', 365 + 'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php', 365 366 'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php', 366 367 'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php', 367 368 'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php', ··· 471 472 'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php', 472 473 'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php', 473 474 'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php', 475 + 'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php', 474 476 'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php', 475 477 'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php', 476 478 'DifferentialNextStepField' => 'applications/differential/customfield/DifferentialNextStepField.php', ··· 4551 4553 'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField', 4552 4554 'DifferentialBlockHeraldAction' => 'HeraldAction', 4553 4555 'DifferentialBranchField' => 'DifferentialCustomField', 4556 + 'DifferentialChangeDetailMailView' => 'DifferentialMailView', 4554 4557 'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup', 4555 4558 'DifferentialChangeType' => 'Phobject', 4556 4559 'DifferentialChangesSinceLastUpdateField' => 'DifferentialCustomField', ··· 4665 4668 'PhabricatorInlineCommentInterface', 4666 4669 ), 4667 4670 'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController', 4668 - 'DifferentialInlineCommentMailView' => 'Phobject', 4671 + 'DifferentialInlineCommentMailView' => 'DifferentialMailView', 4669 4672 'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', 4670 4673 'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery', 4671 4674 'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField', ··· 4676 4679 'DifferentialLintField' => 'DifferentialHarbormasterField', 4677 4680 'DifferentialLintStatus' => 'Phobject', 4678 4681 'DifferentialLocalCommitsView' => 'AphrontView', 4682 + 'DifferentialMailView' => 'Phobject', 4679 4683 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', 4680 4684 'DifferentialModernHunk' => 'DifferentialHunk', 4681 4685 'DifferentialNextStepField' => 'DifferentialCustomField',
+38 -16
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 1251 1251 $config_attach = PhabricatorEnv::getEnvConfig($config_key_attach); 1252 1252 1253 1253 if ($config_inline || $config_attach) { 1254 - $patch_section = $this->renderPatchForMail($diff); 1255 - $lines = count(phutil_split_lines($patch_section->getPlaintext())); 1254 + $patch = $this->buildPatchForMail($diff); 1255 + $lines = substr_count($patch, "\n"); 1256 1256 1257 1257 if ($config_inline && ($lines <= $config_inline)) { 1258 - $body->addTextSection( 1259 - pht('CHANGE DETAILS'), 1260 - $patch_section); 1258 + $this->appendChangeDetailsForMail($object, $diff, $patch, $body); 1261 1259 } 1262 1260 1263 1261 if ($config_attach) { 1264 1262 $name = pht('D%s.%s.patch', $object->getID(), $diff->getID()); 1265 1263 $mime_type = 'text/x-patch; charset=utf-8'; 1266 1264 $body->addAttachment( 1267 - new PhabricatorMetaMTAAttachment( 1268 - $patch_section->getPlaintext(), $name, $mime_type)); 1265 + new PhabricatorMetaMTAAttachment($patch, $name, $mime_type)); 1269 1266 } 1270 1267 } 1271 1268 } ··· 1387 1384 $section_text = "\n".$section->getPlaintext(); 1388 1385 1389 1386 $style = array( 1390 - 'margin: 12px 0;', 1387 + 'margin: 6px 0 12px 0;', 1388 + ); 1389 + 1390 + $section_html = phutil_tag( 1391 + 'div', 1392 + array( 1393 + 'style' => implode(' ', $style), 1394 + ), 1395 + $section->getHTML()); 1396 + 1397 + $body->addPlaintextSection($header, $section_text, false); 1398 + $body->addHTMLSection($header, $section_html); 1399 + } 1400 + 1401 + private function appendChangeDetailsForMail( 1402 + PhabricatorLiskDAO $object, 1403 + DifferentialDiff $diff, 1404 + $patch, 1405 + PhabricatorMetaMTAMailBody $body) { 1406 + 1407 + $section = id(new DifferentialChangeDetailMailView()) 1408 + ->setViewer($this->getActor()) 1409 + ->setDiff($diff) 1410 + ->setPatch($patch) 1411 + ->buildMailSection(); 1412 + 1413 + $header = pht('CHANGE DETAILS'); 1414 + 1415 + $section_text = "\n".$section->getPlaintext(); 1416 + 1417 + $style = array( 1418 + 'margin: 6px 0 12px 0;', 1391 1419 ); 1392 1420 1393 1421 $section_html = phutil_tag( ··· 1659 1687 array('style' => 'font-family: monospace;'), $patch); 1660 1688 } 1661 1689 1662 - private function renderPatchForMail(DifferentialDiff $diff) { 1690 + private function buildPatchForMail(DifferentialDiff $diff) { 1663 1691 $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); 1664 1692 1665 - $patch = id(new DifferentialRawDiffRenderer()) 1693 + return id(new DifferentialRawDiffRenderer()) 1666 1694 ->setViewer($this->getActor()) 1667 1695 ->setFormat($format) 1668 1696 ->setChangesets($diff->getChangesets()) 1669 1697 ->buildPatch(); 1670 - 1671 - $section = new PhabricatorMetaMTAMailSection(); 1672 - $section->addHTMLFragment($this->renderPatchHTMLForMail($patch)); 1673 - $section->addPlaintextFragment($patch); 1674 - 1675 - return $section; 1676 1698 } 1677 1699 1678 1700 protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {
+77
src/applications/differential/mail/DifferentialChangeDetailMailView.php
··· 1 + <?php 2 + 3 + final class DifferentialChangeDetailMailView 4 + extends DifferentialMailView { 5 + 6 + private $viewer; 7 + private $diff; 8 + private $patch; 9 + 10 + public function setViewer(PhabricatorUser $viewer) { 11 + $this->viewer = $viewer; 12 + return $this; 13 + } 14 + 15 + public function getViewer() { 16 + return $this->viewer; 17 + } 18 + 19 + public function setDiff(DifferentialDiff $diff) { 20 + $this->diff = $diff; 21 + return $this; 22 + } 23 + 24 + public function getDiff() { 25 + return $this->diff; 26 + } 27 + 28 + public function setPatch($patch) { 29 + $this->patch = $patch; 30 + return $this; 31 + } 32 + 33 + public function getPatch() { 34 + return $this->patch; 35 + } 36 + 37 + public function buildMailSection() { 38 + $viewer = $this->getViewer(); 39 + 40 + $diff = $this->getDiff(); 41 + 42 + $engine = new PhabricatorMarkupEngine(); 43 + 44 + $out = array(); 45 + foreach ($diff->getChangesets() as $changeset) { 46 + $parser = id(new DifferentialChangesetParser()) 47 + ->setUser($viewer) 48 + ->setChangeset($changeset) 49 + ->setLinesOfContext(2) 50 + ->setMarkupEngine($engine); 51 + 52 + $parser->setRenderer(new DifferentialChangesetOneUpMailRenderer()); 53 + $block = $parser->render(); 54 + 55 + $filename = $changeset->getFilename(); 56 + $filename = $this->renderHeaderBold($filename); 57 + $header = $this->renderHeaderBlock($filename); 58 + 59 + $out[] = $this->renderContentBox( 60 + array( 61 + $header, 62 + $this->renderCodeBlock($block), 63 + )); 64 + } 65 + 66 + $out = phutil_implode_html(phutil_tag('br'), $out); 67 + 68 + $patch_html = $out; 69 + 70 + $patch_text = $this->getPatch(); 71 + 72 + return id(new PhabricatorMetaMTAMailSection()) 73 + ->addPlaintextFragment($patch_text) 74 + ->addHTMLFragment($patch_html); 75 + } 76 + 77 + }
+6 -54
src/applications/differential/mail/DifferentialInlineCommentMailView.php
··· 1 1 <?php 2 2 3 3 final class DifferentialInlineCommentMailView 4 - extends Phobject { 4 + extends DifferentialMailView { 5 5 6 6 private $viewer; 7 7 private $inlines; ··· 85 85 $section->addPlaintextFragment($spacer_text); 86 86 $section->addPlaintextFragment($render_text); 87 87 88 - $style = array( 89 - 'border: 1px solid #C7CCD9;', 90 - 'border-radius: 3px;', 91 - ); 92 - 93 - $html_fragment = phutil_tag( 94 - 'div', 95 - array( 96 - 'style' => implode(' ', $style), 97 - ), 88 + $html_fragment = $this->renderContentBox( 98 89 array( 99 90 $context_html, 100 91 $render_html, ··· 374 365 $is_html) { 375 366 376 367 if ($is_html) { 377 - $style = array( 378 - 'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;', 379 - 'white-space: pre-wrap;', 380 - 'clear: both;', 381 - 'padding: 4px 0;', 382 - 'margin: 0;', 383 - ); 384 - 385 - $style = implode(' ', $style); 386 - $patch = phutil_tag( 387 - 'div', 388 - array( 389 - 'style' => $style, 390 - ), 391 - $patch); 368 + $patch = $this->renderCodeBlock($patch); 392 369 } 393 370 394 371 $header = $this->renderHeader($comment, $is_html, false); ··· 430 407 431 408 $header = "{$path}:{$range}"; 432 409 if ($is_html) { 433 - $header = phutil_tag( 434 - 'span', 435 - array( 436 - 'style' => 'color: #4b4d51; font-weight: bold;', 437 - ), 438 - $header); 410 + $header = $this->renderHeaderBold($header); 439 411 } 440 412 441 413 if ($with_author) { ··· 448 420 $byline = $author->getName(); 449 421 450 422 if ($is_html) { 451 - $byline = phutil_tag( 452 - 'span', 453 - array( 454 - 'style' => 'color: #4b4d51; font-weight: bold;', 455 - ), 456 - $byline); 423 + $byline = $this->renderHeaderBold($byline); 457 424 } 458 425 459 426 $header = pht('%s wrote in %s', $byline, $header); ··· 478 445 $link = null; 479 446 } 480 447 481 - $style = array( 482 - 'color: #74777d;', 483 - 'background: #eff2f4;', 484 - 'padding: 6px 8px;', 485 - 'overflow: hidden;', 486 - ); 487 - 488 - $header = phutil_tag( 489 - 'div', 490 - array( 491 - 'style' => implode(' ', $style), 492 - ), 493 - array( 494 - $link, 495 - $header, 496 - )); 448 + $header = $this->renderHeaderBlock(array($link, $header)); 497 449 } 498 450 499 451 return $header;
+62
src/applications/differential/mail/DifferentialMailView.php
··· 1 + <?php 2 + 3 + abstract class DifferentialMailView 4 + extends Phobject { 5 + 6 + protected function renderCodeBlock($block) { 7 + $style = array( 8 + 'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;', 9 + 'white-space: pre-wrap;', 10 + 'clear: both;', 11 + 'padding: 4px 0;', 12 + 'margin: 0;', 13 + ); 14 + 15 + return phutil_tag( 16 + 'div', 17 + array( 18 + 'style' => implode(' ', $style), 19 + ), 20 + $block); 21 + } 22 + 23 + protected function renderHeaderBlock($block) { 24 + $style = array( 25 + 'color: #74777d;', 26 + 'background: #eff2f4;', 27 + 'padding: 6px 8px;', 28 + 'overflow: hidden;', 29 + ); 30 + 31 + return phutil_tag( 32 + 'div', 33 + array( 34 + 'style' => implode(' ', $style), 35 + ), 36 + $block); 37 + } 38 + 39 + protected function renderHeaderBold($content) { 40 + return phutil_tag( 41 + 'span', 42 + array( 43 + 'style' => 'color: #4b4d51; font-weight: bold;', 44 + ), 45 + $content); 46 + } 47 + 48 + protected function renderContentBox($content) { 49 + $style = array( 50 + 'border: 1px solid #C7CCD9;', 51 + 'border-radius: 3px;', 52 + ); 53 + 54 + return phutil_tag( 55 + 'div', 56 + array( 57 + 'style' => implode(' ', $style), 58 + ), 59 + $content); 60 + } 61 + 62 + }
+20 -6
src/applications/differential/parser/DifferentialChangesetParser.php
··· 55 55 private $rangeStart; 56 56 private $rangeEnd; 57 57 private $mask; 58 + private $linesOfContext = 8; 58 59 59 60 private $highlightEngine; 60 61 ··· 195 196 const ATTR_WHITELINES = 'attr:white'; 196 197 const ATTR_MOVEAWAY = 'attr:moveaway'; 197 198 198 - const LINES_CONTEXT = 8; 199 - 200 199 const WHITESPACE_SHOW_ALL = 'show-all'; 201 200 const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing'; 202 201 const WHITESPACE_IGNORE_MOST = 'ignore-most'; ··· 226 225 $this->visible = $mask; 227 226 return $this; 228 227 } 228 + 229 + public function setLinesOfContext($lines_of_context) { 230 + $this->linesOfContext = $lines_of_context; 231 + return $this; 232 + } 233 + 234 + public function getLinesOfContext() { 235 + return $this->linesOfContext; 236 + } 237 + 229 238 230 239 /** 231 240 * Configure which Changeset comments added to the right side of the visible ··· 724 733 self::ATTR_MOVEAWAY => $moveaway, 725 734 )); 726 735 736 + $lines_context = $this->getLinesOfContext(); 737 + 727 738 $hunk_parser->generateIntraLineDiffs(); 728 - $hunk_parser->generateVisibileLinesMask(); 739 + $hunk_parser->generateVisibileLinesMask($lines_context); 729 740 730 741 $this->setOldLines($hunk_parser->getOldLines()); 731 742 $this->setNewLines($hunk_parser->getNewLines()); ··· 959 970 $old_mask = array(); 960 971 $new_mask = array(); 961 972 $feedback_mask = array(); 973 + $lines_context = $this->getLinesOfContext(); 962 974 963 975 if ($this->comments) { 964 976 // If there are any comments which appear in sections of the file which ··· 1001 1013 1002 1014 } 1003 1015 1004 - $start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0); 1016 + $start = max($comment->getLineNumber() - $lines_context, 0); 1005 1017 $end = $comment->getLineNumber() + 1006 1018 $comment->getLineLength() + 1007 - self::LINES_CONTEXT; 1019 + $lines_context; 1008 1020 for ($ii = $start; $ii <= $end; $ii++) { 1009 1021 if ($new_side) { 1010 1022 $new_mask[$ii] = true; ··· 1189 1201 $range_start, 1190 1202 $range_len) { 1191 1203 1204 + $lines_context = $this->getLinesOfContext(); 1205 + 1192 1206 // Calculate gaps and mask first 1193 1207 $gaps = array(); 1194 1208 $gap_start = 0; ··· 1199 1213 if (isset($base_mask[$ii])) { 1200 1214 if ($in_gap) { 1201 1215 $gap_length = $ii - $gap_start; 1202 - if ($gap_length <= self::LINES_CONTEXT) { 1216 + if ($gap_length <= $lines_context) { 1203 1217 for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) { 1204 1218 $base_mask[$jj] = true; 1205 1219 }
+1 -2
src/applications/differential/parser/DifferentialHunkParser.php
··· 353 353 return $this; 354 354 } 355 355 356 - public function generateVisibileLinesMask() { 357 - $lines_context = DifferentialChangesetParser::LINES_CONTEXT; 356 + public function generateVisibileLinesMask($lines_context) { 358 357 $old = $this->getOldLines(); 359 358 $new = $this->getNewLines(); 360 359 $max_length = max(count($old), count($new));
+19
src/applications/differential/render/DifferentialChangesetOneUpMailRenderer.php
··· 51 51 protected function renderPrimitives(array $primitives, $rows) { 52 52 $out = array(); 53 53 54 + $context_style = array( 55 + 'background: #F7F7F7;', 56 + 'color: #74777D;', 57 + 'border-style: dashed;', 58 + 'border-color: #C7CCD9;', 59 + 'border-width: 1px 0;', 60 + ); 61 + 62 + $context_style = implode(' ', $context_style); 63 + 54 64 foreach ($primitives as $k => $p) { 55 65 $type = $p['type']; 56 66 switch ($type) { ··· 78 88 'style' => $style, 79 89 'render' => $p['render'], 80 90 'text' => (string)$p['render'], 91 + ); 92 + break; 93 + case 'context': 94 + // NOTE: These are being included with no text so they get stripped 95 + // in the header and footer. 96 + $out[] = array( 97 + 'style' => $context_style, 98 + 'render' => '...', 99 + 'text' => '', 81 100 ); 82 101 break; 83 102 default: