mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 22:10:55 +01:00
Flesh out web UI for mail a bit to prepare for Herald outbound rules
Summary: Ref T9141. Ref T5791. Ref T7013. Major changes here is: - Currently, we don't store the headers we actually sent, or the reasons we actually did or did not deliver a mail. - Start storing these (as `headers.sent` and `actors.sent`). - Show them in the web UI. - Show them in `bin/mail show-outbound` (previously, we sort of re-computed them in a hacky way). - Take them into account in `bin/mail volume`. Then some minor changes: - Show mail bodies. - Show more mail information. - Start renaming "MetaMTA" to "Mail", at least in the web UI. Test Plan: {F707501} {F707502} {F707503} {F707504} {F707505} Reviewers: chad Reviewed By: chad Maniphest Tasks: T5791, T7013, T9141 Differential Revision: https://secure.phabricator.com/D13878
This commit is contained in:
parent
9a7beadd22
commit
8c06d89070
6 changed files with 390 additions and 120 deletions
|
@ -3,7 +3,7 @@
|
||||||
final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
return pht('MetaMTA');
|
return pht('Mail');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBaseURI() {
|
public function getBaseURI() {
|
||||||
|
@ -15,11 +15,11 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShortDescription() {
|
public function getShortDescription() {
|
||||||
return pht('Delivers Mail');
|
return pht('Send and Receive Mail');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFlavorText() {
|
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() {
|
public function getApplicationGroup() {
|
||||||
|
@ -30,12 +30,8 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLaunchable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTypeaheadURI() {
|
public function getTypeaheadURI() {
|
||||||
return null;
|
return '/mail/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
|
|
|
@ -4,7 +4,7 @@ final class PhabricatorMetaMTAMailViewController
|
||||||
extends PhabricatorMetaMTAController {
|
extends PhabricatorMetaMTAController {
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getUser();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$mail = id(new PhabricatorMetaMTAMailQuery())
|
$mail = id(new PhabricatorMetaMTAMailQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
@ -19,17 +19,51 @@ final class PhabricatorMetaMTAMailViewController
|
||||||
} else {
|
} else {
|
||||||
$title = $mail->getSubject();
|
$title = $mail->getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($title)
|
->setHeader($title)
|
||||||
->setUser($this->getRequest()->getUser())
|
->setUser($viewer)
|
||||||
->setPolicyObject($mail);
|
->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()
|
$crumbs = $this->buildApplicationCrumbs()
|
||||||
->addTextCrumb(
|
->addTextCrumb(pht('Mail %d', $mail->getID()));
|
||||||
'Mail '.$mail->getID());
|
|
||||||
$object_box = id(new PHUIObjectBoxView())
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->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(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
|
@ -42,42 +76,13 @@ final class PhabricatorMetaMTAMailViewController
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPropertyView(PhabricatorMetaMTAMail $mail) {
|
private function buildMessageProperties(PhabricatorMetaMTAMail $mail) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$properties = id(new PHUIPropertyListView())
|
$properties = id(new PHUIPropertyListView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setObject($mail);
|
->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()) {
|
if ($mail->getFrom()) {
|
||||||
$from_str = $viewer->renderHandle($mail->getFrom());
|
$from_str = $viewer->renderHandle($mail->getFrom());
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,6 +110,167 @@ final class PhabricatorMetaMTAMailViewController
|
||||||
pht('Cc'),
|
pht('Cc'),
|
||||||
$cc_list);
|
$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;
|
return $properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,23 +76,20 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||||
$info[] = pht('Related PHID: %s', $message->getRelatedPHID());
|
$info[] = pht('Related PHID: %s', $message->getRelatedPHID());
|
||||||
$info[] = pht('Message: %s', $message->getMessage());
|
$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[] = null;
|
||||||
$info[] = pht('PARAMETERS');
|
$info[] = pht('PARAMETERS');
|
||||||
$parameters = $message->getParameters();
|
$parameters = $message->getParameters();
|
||||||
foreach ($parameters as $key => $value) {
|
foreach ($parameters as $key => $value) {
|
||||||
if ($key == 'body') {
|
if (isset($ignore[$key])) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($key == 'html-body') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($key == 'headers') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($key == 'attachments') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +102,13 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||||
|
|
||||||
$info[] = null;
|
$info[] = null;
|
||||||
$info[] = pht('HEADERS');
|
$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;
|
list($name, $value) = $header;
|
||||||
$info[] = "{$name}: {$value}";
|
$info[] = "{$name}: {$value}";
|
||||||
}
|
}
|
||||||
|
@ -119,21 +122,33 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$actors = $message->loadAllActors();
|
$all_actors = $message->loadAllActors();
|
||||||
$actors = array_select_keys(
|
|
||||||
$actors,
|
$actors = $message->getDeliveredActors();
|
||||||
array_merge($message->getToPHIDs(), $message->getCcPHIDs()));
|
if ($actors) {
|
||||||
$info[] = null;
|
$info[] = null;
|
||||||
$info[] = pht('RECIPIENTS');
|
$info[] = pht('RECIPIENTS');
|
||||||
foreach ($actors as $actor) {
|
foreach ($actors as $actor_phid => $actor_info) {
|
||||||
if ($actor->isDeliverable()) {
|
$actor = idx($all_actors, $actor_phid);
|
||||||
$info[] = ' '.coalesce($actor->getName(), $actor->getPHID());
|
if ($actor) {
|
||||||
} else {
|
$actor_name = coalesce($actor->getName(), $actor_phid);
|
||||||
$info[] = '! '.coalesce($actor->getName(), $actor->getPHID());
|
} else {
|
||||||
}
|
$actor_name = $actor_phid;
|
||||||
foreach ($actor->getDeliverabilityReasons() as $reason) {
|
}
|
||||||
$desc = PhabricatorMetaMTAActor::getReasonDescription($reason);
|
|
||||||
$info[] = ' - '.$desc;
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,11 @@ final class PhabricatorMailManagementVolumeWorkflow
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$unfiltered = array();
|
$unfiltered = array();
|
||||||
|
$delivered = array();
|
||||||
|
|
||||||
foreach ($mails as $mail) {
|
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');
|
$unfiltered_actors = mpull($mail->loadAllActors(), 'getPHID');
|
||||||
foreach ($unfiltered_actors as $phid) {
|
foreach ($unfiltered_actors as $phid) {
|
||||||
if (empty($unfiltered[$phid])) {
|
if (empty($unfiltered[$phid])) {
|
||||||
|
@ -37,9 +40,26 @@ final class PhabricatorMailManagementVolumeWorkflow
|
||||||
}
|
}
|
||||||
$unfiltered[$phid]++;
|
$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);
|
arsort($unfiltered);
|
||||||
|
$delivered = $delivered + array_fill_keys(array_keys($unfiltered), 0);
|
||||||
|
|
||||||
$table = id(new PhutilConsoleTable())
|
$table = id(new PhutilConsoleTable())
|
||||||
->setBorders(true)
|
->setBorders(true)
|
||||||
|
@ -52,16 +72,23 @@ final class PhabricatorMailManagementVolumeWorkflow
|
||||||
'unfiltered',
|
'unfiltered',
|
||||||
array(
|
array(
|
||||||
'title' => pht('Unfiltered'),
|
'title' => pht('Unfiltered'),
|
||||||
|
))
|
||||||
|
->addColumn(
|
||||||
|
'delivered',
|
||||||
|
array(
|
||||||
|
'title' => pht('Delivered'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$handles = $viewer->loadHandles(array_keys($unfiltered));
|
$handles = $viewer->loadHandles(array_keys($unfiltered));
|
||||||
$names = mpull(iterator_to_array($handles), 'getName', 'getPHID');
|
$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(
|
$table->addRow(
|
||||||
array(
|
array(
|
||||||
'user' => idx($names, $phid),
|
'user' => idx($names, $phid),
|
||||||
'unfiltered' => $count,
|
'unfiltered' => $unfiltered_count,
|
||||||
|
'delivered' => $delivered_count,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +97,9 @@ final class PhabricatorMailManagementVolumeWorkflow
|
||||||
echo "\n";
|
echo "\n";
|
||||||
echo pht('Mail sent in the last 30 days.')."\n";
|
echo pht('Mail sent in the last 30 days.')."\n";
|
||||||
echo pht(
|
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";
|
echo "\n";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -5,6 +5,7 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
||||||
const STATUS_DELIVERABLE = 'deliverable';
|
const STATUS_DELIVERABLE = 'deliverable';
|
||||||
const STATUS_UNDELIVERABLE = 'undeliverable';
|
const STATUS_UNDELIVERABLE = 'undeliverable';
|
||||||
|
|
||||||
|
const REASON_NONE = 'none';
|
||||||
const REASON_UNLOADABLE = 'unloadable';
|
const REASON_UNLOADABLE = 'unloadable';
|
||||||
const REASON_UNMAILABLE = 'unmailable';
|
const REASON_UNMAILABLE = 'unmailable';
|
||||||
const REASON_NO_ADDRESS = 'noaddress';
|
const REASON_NO_ADDRESS = 'noaddress';
|
||||||
|
@ -71,8 +72,42 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
||||||
return $this->reasons;
|
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) {
|
public static function getReasonDescription($reason) {
|
||||||
$descriptions = array(
|
$descriptions = array(
|
||||||
|
self::REASON_NONE => pht(
|
||||||
|
'No special rules affected this mail.'),
|
||||||
self::REASON_DISABLED => pht(
|
self::REASON_DISABLED => pht(
|
||||||
'This user is disabled; disabled users do not receive mail.'),
|
'This user is disabled; disabled users do not receive mail.'),
|
||||||
self::REASON_BOT => pht(
|
self::REASON_BOT => pht(
|
||||||
|
|
|
@ -436,6 +436,8 @@ final class PhabricatorMetaMTAMail
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$headers = $this->generateHeaders();
|
||||||
|
|
||||||
$params = $this->parameters;
|
$params = $this->parameters;
|
||||||
|
|
||||||
$actors = $this->loadAllActors();
|
$actors = $this->loadAllActors();
|
||||||
|
@ -535,16 +537,6 @@ final class PhabricatorMetaMTAMail
|
||||||
$add_cc,
|
$add_cc,
|
||||||
mpull($cc_actors, 'getEmailAddress'));
|
mpull($cc_actors, 'getEmailAddress'));
|
||||||
break;
|
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':
|
case 'attachments':
|
||||||
$value = $this->getAttachments();
|
$value = $this->getAttachments();
|
||||||
foreach ($value as $attachment) {
|
foreach ($value as $attachment) {
|
||||||
|
@ -593,11 +585,6 @@ final class PhabricatorMetaMTAMail
|
||||||
|
|
||||||
$mailer->setSubject(implode(' ', array_filter($subject)));
|
$mailer->setSubject(implode(' ', array_filter($subject)));
|
||||||
break;
|
break;
|
||||||
case 'is-bulk':
|
|
||||||
if ($value) {
|
|
||||||
$mailer->addHeader('Precedence', 'bulk');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'thread-id':
|
case 'thread-id':
|
||||||
|
|
||||||
// NOTE: Gmail freaks out about In-Reply-To and References which
|
// NOTE: Gmail freaks out about In-Reply-To and References which
|
||||||
|
@ -608,7 +595,7 @@ final class PhabricatorMetaMTAMail
|
||||||
$value = '<'.$value.'@'.$domain.'>';
|
$value = '<'.$value.'@'.$domain.'>';
|
||||||
|
|
||||||
if ($is_first && $mailer->supportsMessageIDHeader()) {
|
if ($is_first && $mailer->supportsMessageIDHeader()) {
|
||||||
$mailer->addHeader('Message-ID', $value);
|
$headers[] = array('Message-ID', $value);
|
||||||
} else {
|
} else {
|
||||||
$in_reply_to = $value;
|
$in_reply_to = $value;
|
||||||
$references = array($value);
|
$references = array($value);
|
||||||
|
@ -620,21 +607,16 @@ final class PhabricatorMetaMTAMail
|
||||||
$references[] = $parent_id;
|
$references[] = $parent_id;
|
||||||
}
|
}
|
||||||
$references = implode(' ', $references);
|
$references = implode(' ', $references);
|
||||||
$mailer->addHeader('In-Reply-To', $in_reply_to);
|
$headers[] = array('In-Reply-To', $in_reply_to);
|
||||||
$mailer->addHeader('References', $references);
|
$headers[] = array('References', $references);
|
||||||
}
|
}
|
||||||
$thread_index = $this->generateThreadIndex($value, $is_first);
|
$thread_index = $this->generateThreadIndex($value, $is_first);
|
||||||
$mailer->addHeader('Thread-Index', $thread_index);
|
$headers[] = array('Thread-Index', $thread_index);
|
||||||
break;
|
|
||||||
case 'mailtags':
|
|
||||||
// Handled below.
|
|
||||||
break;
|
|
||||||
case 'subject-prefix':
|
|
||||||
case 'vary-subject-prefix':
|
|
||||||
// Handled above.
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Just discard.
|
// Other parameters are handled elsewhere or are not relevant to
|
||||||
|
// constructing the message.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,6 +642,25 @@ final class PhabricatorMetaMTAMail
|
||||||
$mailer->setHTMLBody($params['html-body']);
|
$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) {
|
if (!$add_to && !$add_cc) {
|
||||||
$this->setStatus(self::STATUS_VOID);
|
$this->setStatus(self::STATUS_VOID);
|
||||||
$this->setMessage(
|
$this->setMessage(
|
||||||
|
@ -692,24 +693,6 @@ final class PhabricatorMetaMTAMail
|
||||||
return $this->save();
|
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
|
// 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:".
|
// don't have any "To:", try to fill it in with a placeholder "To:".
|
||||||
// If that also fails, move the "Cc:" line to "To:".
|
// If that also fails, move the "Cc:" line to "To:".
|
||||||
|
@ -1052,6 +1035,52 @@ final class PhabricatorMetaMTAMail
|
||||||
return $ret;
|
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 )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue