1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 17:22:42 +01:00

Process Remarkup in text and HTML email bodies appropriately

Summary: Ref T6343, adding HTMLMailMode to remarkup, and most objects should now be processed and appear pretty in emails.

Test Plan: Add a comment to a Maniphest task containing a mention of an object like '{T1}' or 'T1'. Emails should show a styled version of the object similar to how the object looks in the context of the Maniphest task in the UI.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: chad, Korvin, epriestley

Maniphest Tasks: T6343, T2617

Differential Revision: https://secure.phabricator.com/D10859
This commit is contained in:
lkassianik 2014-11-17 18:26:18 -08:00 committed by epriestley
parent 81a13ed8bd
commit 1b438a8bd1
11 changed files with 181 additions and 32 deletions

View file

@ -140,6 +140,10 @@ final class DivinerSymbolRemarkupRule extends PhutilRemarkupRule {
$link = $title; $link = $title;
} }
} else if ($href) { } else if ($href) {
if ($this->getEngine()->isHTMLMailMode()) {
$href = PhabricatorEnv::getProductionURI($href);
}
$link = $this->newTag( $link = $this->newTag(
'a', 'a',
array( array(

View file

@ -14,7 +14,11 @@ final class PhabricatorIconRemarkupRule extends PhutilRemarkupRule {
} }
public function markupIcon($matches) { public function markupIcon($matches) {
if (!$this->isFlatText($matches[0])) { $engine = $this->getEngine();
$text_mode = $engine->isTextMode();
$mail_mode = $engine->isHTMLMailMode();
if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
return $matches[0]; return $matches[0];
} }
@ -69,6 +73,7 @@ final class PhabricatorIconRemarkupRule extends PhutilRemarkupRule {
$icon_view = id(new PHUIIconView()) $icon_view = id(new PHUIIconView())
->setIconFont('fa-'.$icon, $color); ->setIconFont('fa-'.$icon, $color);
return $this->getEngine()->storeText($icon_view); return $this->getEngine()->storeText($icon_view);
} }

View file

@ -109,6 +109,8 @@ final class PhabricatorImageMacroRemarkupRule extends PhutilRemarkupRule {
$result = $spec['original'].' <'.$src_uri.'>'; $result = $spec['original'].' <'.$src_uri.'>';
$engine->overwriteStoredText($spec['token'], $result); $engine->overwriteStoredText($spec['token'], $result);
continue; continue;
} else if ($this->getEngine()->isHTMLMailMode()) {
$src_uri = PhabricatorEnv::getProductionURI($src_uri);
} }
$file_data = $file->getMetadata(); $file_data = $file->getMetadata();

View file

@ -34,6 +34,10 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule {
->alter('uppertext', $options['above']) ->alter('uppertext', $options['above'])
->alter('lowertext', $options['below']); ->alter('lowertext', $options['below']);
if ($this->getEngine()->isHTMLMailMode()) {
$uri = PhabricatorEnv::getProductionURI($uri);
}
if ($this->getEngine()->isTextMode()) { if ($this->getEngine()->isTextMode()) {
$img = $img =
($options['above'] != '' ? "\"{$options['above']}\"\n" : ''). ($options['above'] != '' ? "\"{$options['above']}\"\n" : '').

View file

@ -12,6 +12,15 @@ final class PhabricatorMetaMTAMailBody {
private $htmlSections = array(); private $htmlSections = array();
private $attachments = array(); private $attachments = array();
private $viewer;
public function getViewer() {
return $this->viewer;
}
public function setViewer($viewer) {
$this->viewer = $viewer;
}
/* -( Composition )-------------------------------------------------------- */ /* -( Composition )-------------------------------------------------------- */
@ -33,6 +42,39 @@ final class PhabricatorMetaMTAMailBody {
return $this; return $this;
} }
public function addRemarkupSection($text) {
try {
$engine = PhabricatorMarkupEngine::newMarkupEngine(array());
$engine->setConfig('viewer', $this->getViewer());
$engine->setMode(PhutilRemarkupEngine::MODE_TEXT);
$styled_text = $engine->markupText($text);
$this->sections[] = $styled_text;
} catch (Exception $ex) {
phlog($ex);
$this->sections[] = $text;
}
try {
$mail_engine = PhabricatorMarkupEngine::newMarkupEngine(array());
$mail_engine->setConfig('viewer', $this->getViewer());
$mail_engine->setMode(PhutilRemarkupEngine::MODE_HTML_MAIL);
$mail_engine->setConfig(
'uri.base',
PhabricatorEnv::getProductionURI('/'));
$html = $mail_engine->markupText($text);
$this->htmlSections[] = $html;
} catch (Exception $ex) {
phlog($ex);
$this->htmlSections[] = phutil_escape_html_newlines(
phutil_tag(
'div',
array(),
$text));
}
return $this;
}
public function addRawPlaintextSection($text) { public function addRawPlaintextSection($text) {
if (strlen($text)) { if (strlen($text)) {
$text = rtrim($text); $text = rtrim($text);

View file

@ -100,22 +100,41 @@ final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule {
$user = $actual_users[$username]; $user = $actual_users[$username];
Javelin::initBehavior('phabricator-hovercards'); Javelin::initBehavior('phabricator-hovercards');
$tag = id(new PHUITagView()) $user_href = '/p/'.$user->getUserName().'/';
->setType(PHUITagView::TYPE_PERSON)
->setPHID($user->getPHID())
->setName('@'.$user->getUserName())
->setHref('/p/'.$user->getUserName().'/');
if (!$user->isUserActivated()) { if ($engine->isHTMLMailMode()) {
$tag->setDotColor(PHUITagView::COLOR_GREY); $user_href = PhabricatorEnv::getProductionURI($user_href);
$tag = phutil_tag(
'a',
array(
'href' => $user_href,
'style' => 'background-color: #f1f7ff;
border-color: #f1f7ff;
border: 1px solid transparent;
border-radius: 3px;
color: #19558d;
font-weight: bold;
padding: 0 4px;',
),
'@'.$user->getUserName());
} else { } else {
$status = idx($user_statuses, $user->getPHID()); $tag = id(new PHUITagView())
if ($status) { ->setType(PHUITagView::TYPE_PERSON)
$status = $status->getStatus(); ->setPHID($user->getPHID())
if ($status == PhabricatorCalendarEvent::STATUS_AWAY) { ->setName('@'.$user->getUserName())
$tag->setDotColor(PHUITagView::COLOR_RED); ->setHref($user_href);
} else if ($status == PhabricatorCalendarEvent::STATUS_AWAY) {
$tag->setDotColor(PHUITagView::COLOR_ORANGE); if (!$user->isUserActivated()) {
$tag->setDotColor(PHUITagView::COLOR_GREY);
} else {
$status = idx($user_statuses, $user->getPHID());
if ($status) {
$status = $status->getStatus();
if ($status == PhabricatorCalendarEvent::STATUS_AWAY) {
$tag->setDotColor(PHUITagView::COLOR_RED);
} else if ($status == PhabricatorCalendarEvent::STATUS_AWAY) {
$tag->setDotColor(PHUITagView::COLOR_ORANGE);
}
} }
} }
} }

View file

@ -29,9 +29,12 @@ final class PhrictionRemarkupRule extends PhutilRemarkupRule {
$slug = PhrictionDocument::getSlugURI($slug); $slug = PhrictionDocument::getSlugURI($slug);
$href = (string)id(new PhutilURI($slug))->setFragment($fragment); $href = (string)id(new PhutilURI($slug))->setFragment($fragment);
$text_mode = $this->getEngine()->isTextMode();
$mail_mode = $this->getEngine()->isHTMLMailMode();
if ($this->getEngine()->getState('toc')) { if ($this->getEngine()->getState('toc')) {
$text = $name; $text = $name;
} else if ($this->getEngine()->isTextMode()) { } else if ($text_mode || $mail_mode) {
return PhabricatorEnv::getProductionURI($href); return PhabricatorEnv::getProductionURI($href);
} else { } else {
$text = $this->newTag( $text = $this->newTag(

View file

@ -2159,10 +2159,11 @@ abstract class PhabricatorApplicationTransactionEditor
} }
$body = new PhabricatorMetaMTAMailBody(); $body = new PhabricatorMetaMTAMailBody();
$body->setViewer($this->requireActor());
$body->addRawSection(implode("\n", $headers)); $body->addRawSection(implode("\n", $headers));
foreach ($comments as $comment) { foreach ($comments as $comment) {
$body->addRawSection($comment); $body->addRemarkupSection($comment);
} }
if ($object instanceof PhabricatorCustomFieldInterface) { if ($object instanceof PhabricatorCustomFieldInterface) {

View file

@ -80,20 +80,30 @@ final class PhabricatorNavigationRemarkupRule extends PhutilRemarkupRule {
$out[] = $tag; $out[] = $tag;
} }
if ($this->getEngine()->isHTMLMailMode()) {
$arrow_attr = array(
'style' => 'color: #92969D;',
);
$nav_attr = array();
} else {
$arrow_attr = array(
'class' => 'remarkup-nav-sequence-arrow',
);
$nav_attr = array(
'class' => 'remarkup-nav-sequence',
);
}
$joiner = phutil_tag( $joiner = phutil_tag(
'span', 'span',
array( $arrow_attr,
'class' => 'remarkup-nav-sequence-arrow',
),
" \xE2\x86\x92 "); " \xE2\x86\x92 ");
$out = phutil_implode_html($joiner, $out); $out = phutil_implode_html($joiner, $out);
$out = phutil_tag( $out = phutil_tag(
'span', 'span',
array( $nav_attr,
'class' => 'remarkup-nav-sequence',
),
$out); $out);
return $this->getEngine()->storeText($out); return $this->getEngine()->storeText($out);

View file

@ -44,7 +44,11 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
return $handle->getURI(); return $handle->getURI();
} }
protected function renderObjectRef($object, $handle, $anchor, $id) { protected function renderObjectRefForAnyMedia (
$object,
$handle,
$anchor,
$id) {
$href = $this->getObjectHref($object, $handle, $id); $href = $this->getObjectHref($object, $handle, $id);
$text = $this->getObjectNamePrefix().$id; $text = $this->getObjectNamePrefix().$id;
@ -55,10 +59,25 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
if ($this->getEngine()->isTextMode()) { if ($this->getEngine()->isTextMode()) {
return PhabricatorEnv::getProductionURI($href); return PhabricatorEnv::getProductionURI($href);
} else if ($this->getEngine()->isHTMLMailMode()) {
$href = PhabricatorEnv::getProductionURI($href);
return $this->renderObjectTagForMail($text, $href, $handle);
} }
return $this->renderObjectRef($object, $handle, $anchor, $id);
}
protected function renderObjectRef($object, $handle, $anchor, $id) {
$href = $this->getObjectHref($object, $handle, $id);
$text = $this->getObjectNamePrefix().$id;
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED; $status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
if ($anchor) {
$href = $href.'#'.$anchor;
$text = $text.'#'.$anchor;
}
$attr = array( $attr = array(
'phid' => $handle->getPHID(), 'phid' => $handle->getPHID(),
'closed' => ($handle->getStatus() == $status_closed), 'closed' => ($handle->getStatus() == $status_closed),
@ -67,15 +86,24 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
return $this->renderHovertag($text, $href, $attr); return $this->renderHovertag($text, $href, $attr);
} }
protected function renderObjectEmbedForAnyMedia($object, $handle, $options) {
$name = $handle->getFullName();
$href = $handle->getURI();
if ($this->getEngine()->isTextMode()) {
return $name.' <'.PhabricatorEnv::getProductionURI($href).'>';
} else if ($this->getEngine()->isHTMLMailMode()) {
$href = PhabricatorEnv::getProductionURI($href);
return $this->renderObjectTagForMail($name, $href, $handle);
}
return $this->renderObjectEmbed($object, $handle, $options);
}
protected function renderObjectEmbed($object, $handle, $options) { protected function renderObjectEmbed($object, $handle, $options) {
$name = $handle->getFullName(); $name = $handle->getFullName();
$href = $handle->getURI(); $href = $handle->getURI();
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED; $status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
if ($this->getEngine()->isTextMode()) {
return $name.' <'.PhabricatorEnv::getProductionURI($href).'>';
}
$attr = array( $attr = array(
'phid' => $handle->getPHID(), 'phid' => $handle->getPHID(),
'closed' => ($handle->getStatus() == $status_closed), 'closed' => ($handle->getStatus() == $status_closed),
@ -84,6 +112,31 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
return $this->renderHovertag($name, $href, $attr); return $this->renderHovertag($name, $href, $attr);
} }
protected function renderObjectTagForMail(
$text,
$href,
$handle) {
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
$strikethrough = $handle->getStatus() == $status_closed ?
'text-decoration: line-through;' :
'text-decoration: none;';
return phutil_tag(
'a',
array(
'href' => $href,
'style' => 'background-color: #e7e7e7;
border-color: #e7e7e7;
border-radius: 3px;
padding: 0 4px;
font-weight: bold;
color: black;'
.$strikethrough,
),
$text);
}
protected function renderHovertag($name, $href, array $attr = array()) { protected function renderHovertag($name, $href, array $attr = array()) {
return id(new PHUITagView()) return id(new PHUITagView())
->setName($name) ->setName($name)
@ -282,7 +335,8 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
$object = $objects[$spec['id']]; $object = $objects[$spec['id']];
switch ($spec['type']) { switch ($spec['type']) {
case 'ref': case 'ref':
$view = $this->renderObjectRef(
$view = $this->renderObjectRefForAnyMedia(
$object, $object,
$handle, $handle,
$spec['anchor'], $spec['anchor'],
@ -290,7 +344,10 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
break; break;
case 'embed': case 'embed':
$spec['options'] = $this->assertFlatText($spec['options']); $spec['options'] = $this->assertFlatText($spec['options']);
$view = $this->renderObjectEmbed($object, $handle, $spec['options']); $view = $this->renderObjectEmbedForAnyMedia(
$object,
$handle,
$spec['options']);
break; break;
} }
$engine->overwriteStoredText($spec['token'], $view); $engine->overwriteStoredText($spec['token'], $view);

View file

@ -20,8 +20,10 @@ final class PhabricatorYoutubeRemarkupRule extends PhutilRemarkupRule {
public function markupYoutubeLink() { public function markupYoutubeLink() {
$v = idx($this->uri->getQueryParams(), 'v'); $v = idx($this->uri->getQueryParams(), 'v');
$text_mode = $this->getEngine()->isTextMode();
$mail_mode = $this->getEngine()->isHTMLMailMode();
if ($this->getEngine()->isTextMode()) { if ($text_mode || $mail_mode) {
return $this->getEngine()->storeText('http://youtu.be/'.$v); return $this->getEngine()->storeText('http://youtu.be/'.$v);
} }