Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15485363
D13878.id33511.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
23 KB
Referenced Files
None
Subscribers
None
D13878.id33511.diff
View Options
diff --git a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php
--- a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php
+++ b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php
@@ -3,7 +3,7 @@
final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
public function getName() {
- return pht('MetaMTA');
+ return pht('Mail');
}
public function getBaseURI() {
@@ -15,11 +15,11 @@
}
public function getShortDescription() {
- return pht('Delivers Mail');
+ return pht('Send and Receive Mail');
}
public function getFlavorText() {
- return pht('Yo dawg, we heard you like MTAs.');
+ return pht('Every program attempts to expand until it can read mail.');
}
public function getApplicationGroup() {
@@ -30,12 +30,8 @@
return false;
}
- public function isLaunchable() {
- return false;
- }
-
public function getTypeaheadURI() {
- return null;
+ return '/mail/';
}
public function getRoutes() {
diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php
--- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php
+++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php
@@ -4,7 +4,7 @@
extends PhabricatorMetaMTAController {
public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getUser();
+ $viewer = $this->getViewer();
$mail = id(new PhabricatorMetaMTAMailQuery())
->setViewer($viewer)
@@ -19,17 +19,51 @@
} else {
$title = $mail->getSubject();
}
+
$header = id(new PHUIHeaderView())
->setHeader($title)
- ->setUser($this->getRequest()->getUser())
+ ->setUser($viewer)
->setPolicyObject($mail);
+ switch ($mail->getStatus()) {
+ case PhabricatorMetaMTAMail::STATUS_QUEUE:
+ $icon = 'fa-clock-o';
+ $color = 'blue';
+ $name = pht('Queued');
+ break;
+ case PhabricatorMetaMTAMail::STATUS_SENT:
+ $icon = 'fa-envelope';
+ $color = 'green';
+ $name = pht('Sent');
+ break;
+ case PhabricatorMetaMTAMail::STATUS_FAIL:
+ $icon = 'fa-envelope';
+ $color = 'red';
+ $name = pht('Delivery Failed');
+ break;
+ case PhabricatorMetaMTAMail::STATUS_VOID:
+ $icon = 'fa-envelope';
+ $color = 'black';
+ $name = pht('Voided');
+ break;
+ default:
+ $icon = 'fa-question-circle';
+ $color = 'yellow';
+ $name = pht('Unknown');
+ break;
+ }
+
+ $header->setStatus($icon, $color, $name);
+
$crumbs = $this->buildApplicationCrumbs()
- ->addTextCrumb(
- 'Mail '.$mail->getID());
+ ->addTextCrumb(pht('Mail %d', $mail->getID()));
+
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
- ->addPropertyList($this->buildPropertyView($mail));
+ ->addPropertyList($this->buildMessageProperties($mail), pht('Message'))
+ ->addPropertyList($this->buildHeaderProperties($mail), pht('Headers'))
+ ->addPropertyList($this->buildDeliveryProperties($mail), pht('Delivery'))
+ ->addPropertyList($this->buildMetadataProperties($mail), pht('Metadata'));
return $this->buildApplicationPage(
array(
@@ -42,42 +76,13 @@
));
}
- private function buildPropertyView(PhabricatorMetaMTAMail $mail) {
+ private function buildMessageProperties(PhabricatorMetaMTAMail $mail) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($mail);
- $properties->addProperty(
- pht('ID'),
- $mail->getID());
-
- $properties->addProperty(
- pht('Status'),
- $mail->getStatus());
-
- if ($mail->getMessage()) {
- $properties->addProperty(
- pht('Status Details'),
- $mail->getMessage());
- }
-
- if ($mail->getRelatedPHID()) {
- $properties->addProperty(
- pht('Related Object'),
- $viewer->renderHandle($mail->getRelatedPHID()));
- }
-
- if ($mail->getActorPHID()) {
- $actor_str = $viewer->renderHandle($mail->getActorPHID());
- } else {
- $actor_str = pht('Generated by Phabricator');
- }
- $properties->addProperty(
- pht('Actor'),
- $actor_str);
-
if ($mail->getFrom()) {
$from_str = $viewer->renderHandle($mail->getFrom());
} else {
@@ -105,6 +110,167 @@
pht('Cc'),
$cc_list);
+ $properties->addSectionHeader(
+ pht('Message'),
+ PHUIPropertyListView::ICON_SUMMARY);
+
+ if ($mail->hasSensitiveContent()) {
+ $body = phutil_tag(
+ 'em',
+ array(),
+ pht(
+ 'The content of this mail is sensitive and it can not be '.
+ 'viewed from the web UI.'));
+ } else {
+ $body = phutil_tag(
+ 'div',
+ array(
+ 'style' => 'white-space: pre-wrap',
+ ),
+ $mail->getBody());
+ }
+
+ $properties->addTextContent($body);
+
+
+ return $properties;
+ }
+
+ private function buildHeaderProperties(PhabricatorMetaMTAMail $mail) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setStacked(true);
+
+ $headers = $mail->getDeliveredHeaders();
+ if ($headers === null) {
+ $headers = $mail->generateHeaders();
+ }
+
+ // Sort headers by name.
+ $headers = isort($headers, 0);
+
+ foreach ($headers as $header) {
+ list($key, $value) = $header;
+ $properties->addProperty($key, $value);
+ }
+
+ return $properties;
+ }
+
+ private function buildDeliveryProperties(PhabricatorMetaMTAMail $mail) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer);
+
+ $actors = $mail->getDeliveredActors();
+ $reasons = null;
+ if (!$actors) {
+ // TODO: We can get rid of this special-cased message after these changes
+ // have been live for a while, but provide a more tailored message for
+ // now so things are a little less confusing for users.
+ if ($mail->getStatus() == PhabricatorMetaMTAMail::STATUS_SENT) {
+ $delivery = phutil_tag(
+ 'em',
+ array(),
+ pht(
+ 'This is an older message that predates recording delivery '.
+ 'information, so none is available.'));
+ } else {
+ $delivery = phutil_tag(
+ 'em',
+ array(),
+ pht(
+ 'This message has not been delivered yet, so delivery information '.
+ 'is not available.'));
+ }
+ } else {
+ $actor = idx($actors, $viewer->getPHID());
+ if (!$actor) {
+ $delivery = phutil_tag(
+ 'em',
+ array(),
+ pht('This message was not delivered to you.'));
+ } else {
+ $deliverable = $actor['deliverable'];
+ if ($deliverable) {
+ $delivery = pht('Delivered');
+ } else {
+ $delivery = pht('Voided');
+ }
+
+ $reasons = id(new PHUIStatusListView());
+
+ $reason_codes = $actor['reasons'];
+ if (!$reason_codes) {
+ $reason_codes = array(
+ PhabricatorMetaMTAActor::REASON_NONE,
+ );
+ }
+
+ $icon_yes = 'fa-check green';
+ $icon_no = 'fa-times red';
+
+ foreach ($reason_codes as $reason) {
+ $target = phutil_tag(
+ 'strong',
+ array(),
+ PhabricatorMetaMTAActor::getReasonName($reason));
+
+ if (PhabricatorMetaMTAActor::isDeliveryReason($reason)) {
+ $icon = $icon_yes;
+ } else {
+ $icon = $icon_no;
+ }
+
+ $item = id(new PHUIStatusItemView())
+ ->setIcon($icon)
+ ->setTarget($target)
+ ->setNote(PhabricatorMetaMTAActor::getReasonDescription($reason));
+
+ $reasons->addItem($item);
+ }
+ }
+ }
+
+ $properties->addProperty(pht('Delivery'), $delivery);
+ if ($reasons) {
+ $properties->addProperty(pht('Reasons'), $reasons);
+ }
+
+ return $properties;
+ }
+
+ private function buildMetadataProperties(PhabricatorMetaMTAMail $mail) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer);
+
+ $details = $mail->getMessage();
+ if (!strlen($details)) {
+ $details = phutil_tag('em', array(), pht('None'));
+ }
+ $properties->addProperty(pht('Status Details'), $details);
+
+ $actor_phid = $mail->getActorPHID();
+ if ($actor_phid) {
+ $actor_str = $viewer->renderHandle($actor_phid);
+ } else {
+ $actor_str = pht('Generated by Phabricator');
+ }
+ $properties->addProperty(pht('Actor'), $actor_str);
+
+ $related_phid = $mail->getRelatedPHID();
+ if ($related_phid) {
+ $related = $viewer->renderHandle($mail->getRelatedPHID());
+ } else {
+ $related = phutil_tag('em', array(), pht('None'));
+ }
+ $properties->addProperty(pht('Related Object'), $related);
+
return $properties;
}
diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php
--- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php
+++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php
@@ -76,23 +76,20 @@
$info[] = pht('Related PHID: %s', $message->getRelatedPHID());
$info[] = pht('Message: %s', $message->getMessage());
+ $ignore = array(
+ 'body' => true,
+ 'html-body' => true,
+ 'headers' => true,
+ 'attachments' => true,
+ 'headers.sent' => true,
+ 'authors.sent' => true,
+ );
+
$info[] = null;
$info[] = pht('PARAMETERS');
$parameters = $message->getParameters();
foreach ($parameters as $key => $value) {
- if ($key == 'body') {
- continue;
- }
-
- if ($key == 'html-body') {
- continue;
- }
-
- if ($key == 'headers') {
- continue;
- }
-
- if ($key == 'attachments') {
+ if (isset($ignore[$key])) {
continue;
}
@@ -105,7 +102,13 @@
$info[] = null;
$info[] = pht('HEADERS');
- foreach (idx($parameters, 'headers', array()) as $header) {
+
+ $headers = $message->getDeliveredHeaders();
+ if (!$headers) {
+ $headers = $message->generateHeaders();
+ }
+
+ foreach ($headers as $header) {
list($name, $value) = $header;
$info[] = "{$name}: {$value}";
}
@@ -119,21 +122,33 @@
}
}
- $actors = $message->loadAllActors();
- $actors = array_select_keys(
- $actors,
- array_merge($message->getToPHIDs(), $message->getCcPHIDs()));
- $info[] = null;
- $info[] = pht('RECIPIENTS');
- foreach ($actors as $actor) {
- if ($actor->isDeliverable()) {
- $info[] = ' '.coalesce($actor->getName(), $actor->getPHID());
- } else {
- $info[] = '! '.coalesce($actor->getName(), $actor->getPHID());
- }
- foreach ($actor->getDeliverabilityReasons() as $reason) {
- $desc = PhabricatorMetaMTAActor::getReasonDescription($reason);
- $info[] = ' - '.$desc;
+ $all_actors = $message->loadAllActors();
+
+ $actors = $message->getDeliveredActors();
+ if ($actors) {
+ $info[] = null;
+ $info[] = pht('RECIPIENTS');
+ foreach ($actors as $actor_phid => $actor_info) {
+ $actor = idx($all_actors, $actor_phid);
+ if ($actor) {
+ $actor_name = coalesce($actor->getName(), $actor_phid);
+ } else {
+ $actor_name = $actor_phid;
+ }
+
+ $deliverable = $actor_info['deliverable'];
+ if ($deliverable) {
+ $info[] = ' '.$actor_name;
+ } else {
+ $info[] = '! '.$actor_name;
+ }
+
+ $reasons = $actor_info['reasons'];
+ foreach ($reasons as $reason) {
+ $name = PhabricatorMetaMTAActor::getReasonName($reason);
+ $desc = PhabricatorMetaMTAActor::getReasonDescription($reason);
+ $info[] = ' - '.$name.': '.$desc;
+ }
}
}
diff --git a/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php
--- a/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php
+++ b/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php
@@ -28,8 +28,11 @@
->execute();
$unfiltered = array();
+ $delivered = array();
foreach ($mails as $mail) {
+ // Count messages we attempted to deliver. This includes messages which
+ // were voided by preferences or other rules.
$unfiltered_actors = mpull($mail->loadAllActors(), 'getPHID');
foreach ($unfiltered_actors as $phid) {
if (empty($unfiltered[$phid])) {
@@ -37,9 +40,26 @@
}
$unfiltered[$phid]++;
}
+
+ // Now, count mail we actually delivered.
+ $result = $mail->getDeliveredActors();
+ if ($result) {
+ foreach ($result as $actor_phid => $actor_info) {
+ if (!$actor_info['deliverable']) {
+ continue;
+ }
+ if (empty($delivered[$actor_phid])) {
+ $delivered[$actor_phid] = 0;
+ }
+ $delivered[$actor_phid]++;
+ }
+ }
}
+ // Sort users by delivered mail, then unfiltered mail.
+ arsort($delivered);
arsort($unfiltered);
+ $delivered = $delivered + array_fill_keys(array_keys($unfiltered), 0);
$table = id(new PhutilConsoleTable())
->setBorders(true)
@@ -52,16 +72,23 @@
'unfiltered',
array(
'title' => pht('Unfiltered'),
+ ))
+ ->addColumn(
+ 'delivered',
+ array(
+ 'title' => pht('Delivered'),
));
$handles = $viewer->loadHandles(array_keys($unfiltered));
$names = mpull(iterator_to_array($handles), 'getName', 'getPHID');
- foreach ($unfiltered as $phid => $count) {
+ foreach ($delivered as $phid => $delivered_count) {
+ $unfiltered_count = idx($unfiltered, $phid, 0);
$table->addRow(
array(
'user' => idx($names, $phid),
- 'unfiltered' => $count,
+ 'unfiltered' => $unfiltered_count,
+ 'delivered' => $delivered_count,
));
}
@@ -70,7 +97,9 @@
echo "\n";
echo pht('Mail sent in the last 30 days.')."\n";
echo pht(
- '"Unfiltered" is raw volume before preferences were applied.')."\n";
+ '"Unfiltered" is raw volume before rules applied.')."\n";
+ echo pht(
+ '"Delivered" shows email actually sent.')."\n";
echo "\n";
return 0;
diff --git a/src/applications/metamta/query/PhabricatorMetaMTAActor.php b/src/applications/metamta/query/PhabricatorMetaMTAActor.php
--- a/src/applications/metamta/query/PhabricatorMetaMTAActor.php
+++ b/src/applications/metamta/query/PhabricatorMetaMTAActor.php
@@ -5,6 +5,7 @@
const STATUS_DELIVERABLE = 'deliverable';
const STATUS_UNDELIVERABLE = 'undeliverable';
+ const REASON_NONE = 'none';
const REASON_UNLOADABLE = 'unloadable';
const REASON_UNMAILABLE = 'unmailable';
const REASON_NO_ADDRESS = 'noaddress';
@@ -71,8 +72,42 @@
return $this->reasons;
}
+ public static function isDeliveryReason($reason) {
+ switch ($reason) {
+ case self::REASON_NONE:
+ case self::REASON_FORCE:
+ case self::REASON_FORCE_HERALD:
+ return true;
+ default:
+ // All other reasons cause the message to not be delivered.
+ return false;
+ }
+ }
+
+ public static function getReasonName($reason) {
+ $names = array(
+ self::REASON_NONE => pht('None'),
+ self::REASON_DISABLED => pht('Disabled Recipient'),
+ self::REASON_BOT => pht('Bot Recipient'),
+ self::REASON_NO_ADDRESS => pht('No Address'),
+ self::REASON_EXTERNAL_TYPE => pht('External Recipient'),
+ self::REASON_UNMAILABLE => pht('Not Mailable'),
+ self::REASON_RESPONSE => pht('Similar Reply'),
+ self::REASON_SELF => pht('Self Mail'),
+ self::REASON_MAIL_DISABLED => pht('Mail Disabled'),
+ self::REASON_MAILTAGS => pht('Mail Tags'),
+ self::REASON_UNLOADABLE => pht('Bad Recipient'),
+ self::REASON_FORCE => pht('Forced Mail'),
+ self::REASON_FORCE_HERALD => pht('Forced by Herald'),
+ );
+
+ return idx($names, $reason, pht('Unknown ("%s")', $reason));
+ }
+
public static function getReasonDescription($reason) {
$descriptions = array(
+ self::REASON_NONE => pht(
+ 'No special rules affected this mail.'),
self::REASON_DISABLED => pht(
'This user is disabled; disabled users do not receive mail.'),
self::REASON_BOT => pht(
diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
--- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
+++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
@@ -436,6 +436,8 @@
}
try {
+ $headers = $this->generateHeaders();
+
$params = $this->parameters;
$actors = $this->loadAllActors();
@@ -535,16 +537,6 @@
$add_cc,
mpull($cc_actors, 'getEmailAddress'));
break;
- case 'headers':
- foreach ($value as $pair) {
- list($header_key, $header_value) = $pair;
-
- // NOTE: If we have \n in a header, SES rejects the email.
- $header_value = str_replace("\n", ' ', $header_value);
-
- $mailer->addHeader($header_key, $header_value);
- }
- break;
case 'attachments':
$value = $this->getAttachments();
foreach ($value as $attachment) {
@@ -593,11 +585,6 @@
$mailer->setSubject(implode(' ', array_filter($subject)));
break;
- case 'is-bulk':
- if ($value) {
- $mailer->addHeader('Precedence', 'bulk');
- }
- break;
case 'thread-id':
// NOTE: Gmail freaks out about In-Reply-To and References which
@@ -608,7 +595,7 @@
$value = '<'.$value.'@'.$domain.'>';
if ($is_first && $mailer->supportsMessageIDHeader()) {
- $mailer->addHeader('Message-ID', $value);
+ $headers[] = array('Message-ID', $value);
} else {
$in_reply_to = $value;
$references = array($value);
@@ -620,21 +607,16 @@
$references[] = $parent_id;
}
$references = implode(' ', $references);
- $mailer->addHeader('In-Reply-To', $in_reply_to);
- $mailer->addHeader('References', $references);
+ $headers[] = array('In-Reply-To', $in_reply_to);
+ $headers[] = array('References', $references);
}
$thread_index = $this->generateThreadIndex($value, $is_first);
- $mailer->addHeader('Thread-Index', $thread_index);
- break;
- case 'mailtags':
- // Handled below.
- break;
- case 'subject-prefix':
- case 'vary-subject-prefix':
- // Handled above.
+ $headers[] = array('Thread-Index', $thread_index);
break;
default:
- // Just discard.
+ // Other parameters are handled elsewhere or are not relevant to
+ // constructing the message.
+ break;
}
}
@@ -660,6 +642,25 @@
$mailer->setHTMLBody($params['html-body']);
}
+ // Pass the headers to the mailer, then save the state so we can show
+ // them in the web UI.
+ foreach ($headers as $header) {
+ list($header_key, $header_value) = $header;
+ $mailer->addHeader($header_key, $header_value);
+ }
+ $this->setParam('headers.sent', $headers);
+
+ // Save the final deliverability outcomes and reasoning so we can
+ // explain why things happened the way they did.
+ $actor_list = array();
+ foreach ($actors as $actor) {
+ $actor_list[$actor->getPHID()] = array(
+ 'deliverable' => $actor->isDeliverable(),
+ 'reasons' => $actor->getDeliverabilityReasons(),
+ );
+ }
+ $this->setParam('actors.sent', $actor_list);
+
if (!$add_to && !$add_cc) {
$this->setStatus(self::STATUS_VOID);
$this->setMessage(
@@ -692,24 +693,6 @@
return $this->save();
}
- $mailer->addHeader('X-Phabricator-Sent-This-Message', 'Yes');
- $mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA');
-
- // Some clients respect this to suppress OOF and other auto-responses.
- $mailer->addHeader('X-Auto-Response-Suppress', 'All');
-
- // If the message has mailtags, filter out any recipients who don't want
- // to receive this type of mail.
- $mailtags = $this->getParam('mailtags');
- if ($mailtags) {
- $tag_header = array();
- foreach ($mailtags as $mailtag) {
- $tag_header[] = '<'.$mailtag.'>';
- }
- $tag_header = implode(', ', $tag_header);
- $mailer->addHeader('X-Phabricator-Mail-Tags', $tag_header);
- }
-
// Some mailers require a valid "To:" in order to deliver mail. If we
// don't have any "To:", try to fill it in with a placeholder "To:".
// If that also fails, move the "Cc:" line to "To:".
@@ -1052,6 +1035,52 @@
return $ret;
}
+ public function generateHeaders() {
+ $headers = array();
+
+ $headers[] = array('X-Phabricator-Sent-This-Message', 'Yes');
+ $headers[] = array('X-Mail-Transport-Agent', 'MetaMTA');
+
+ // Some clients respect this to suppress OOF and other auto-responses.
+ $headers[] = array('X-Auto-Response-Suppress', 'All');
+
+ // If the message has mailtags, filter out any recipients who don't want
+ // to receive this type of mail.
+ $mailtags = $this->getParam('mailtags');
+ if ($mailtags) {
+ $tag_header = array();
+ foreach ($mailtags as $mailtag) {
+ $tag_header[] = '<'.$mailtag.'>';
+ }
+ $tag_header = implode(', ', $tag_header);
+ $headers[] = array('X-Phabricator-Mail-Tags', $tag_header);
+ }
+
+ $value = $this->getParam('headers', array());
+ foreach ($value as $pair) {
+ list($header_key, $header_value) = $pair;
+
+ // NOTE: If we have \n in a header, SES rejects the email.
+ $header_value = str_replace("\n", ' ', $header_value);
+ $headers[] = array($header_key, $header_value);
+ }
+
+ $is_bulk = $this->getParam('is-bulk');
+ if ($is_bulk) {
+ $headers[] = array('Precedence', 'bulk');
+ }
+
+ return $headers;
+ }
+
+ public function getDeliveredHeaders() {
+ return $this->getParam('headers.sent');
+ }
+
+ public function getDeliveredActors() {
+ return $this->getParam('actors.sent');
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 10, 11:14 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7610494
Default Alt Text
D13878.id33511.diff (23 KB)
Attached To
Mode
D13878: Flesh out web UI for mail a bit to prepare for Herald outbound rules
Attached
Detach File
Event Timeline
Log In to Comment