diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2247,6 +2247,9 @@ 'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php', 'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php', 'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php', + 'PhabricatorMailEmailHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailHeraldField.php', + 'PhabricatorMailEmailHeraldFieldGroup' => 'applications/metamta/herald/PhabricatorMailEmailHeraldFieldGroup.php', + 'PhabricatorMailEmailSubjectHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailSubjectHeraldField.php', 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php', 'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php', 'PhabricatorMailImplementationMailgunAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php', @@ -2263,10 +2266,15 @@ 'PhabricatorMailManagementShowOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php', 'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php', 'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php', + 'PhabricatorMailOutboundMailHeraldAdapter' => 'applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php', + 'PhabricatorMailOutboundRoutingHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php', + 'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php', + 'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php', 'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php', 'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php', 'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', 'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', + 'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php', 'PhabricatorMailSetupCheck' => 'applications/config/check/PhabricatorMailSetupCheck.php', 'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php', 'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php', @@ -6178,6 +6186,9 @@ 'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorMacroViewController' => 'PhabricatorMacroController', + 'PhabricatorMailEmailHeraldField' => 'HeraldField', + 'PhabricatorMailEmailHeraldFieldGroup' => 'HeraldFieldGroup', + 'PhabricatorMailEmailSubjectHeraldField' => 'PhabricatorMailEmailHeraldField', 'PhabricatorMailImplementationAdapter' => 'Phobject', 'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter', 'PhabricatorMailImplementationMailgunAdapter' => 'PhabricatorMailImplementationAdapter', @@ -6194,10 +6205,15 @@ 'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter', + 'PhabricatorMailOutboundRoutingHeraldAction' => 'HeraldAction', + 'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', + 'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', 'PhabricatorMailOutboundStatus' => 'Phobject', 'PhabricatorMailReceiver' => 'Phobject', 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 'PhabricatorMailReplyHandler' => 'Phobject', + 'PhabricatorMailRoutingRule' => 'Phobject', 'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorMailTarget' => 'Phobject', 'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions', diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -48,6 +48,9 @@ } else if ($object instanceof PonderQuestion) { $adapter = id(new HeraldPonderQuestionAdapter()) ->setQuestion($object); + } else if ($object instanceof PhabricatorMetaMTAMail) { + $adapter = id(new PhabricatorMailOutboundMailHeraldAdapter()) + ->setObject($object); } else { throw new Exception(pht('Can not build adapter for object!')); } diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -356,7 +356,17 @@ // Handle older transcripts which used a static string to record // action results. - if (!is_array($log)) { + + if ($xscript->getDryRun()) { + $action_list->addItem( + id(new PHUIStatusItemView()) + ->setIcon('fa-ban', 'grey') + ->setTarget(pht('Dry Run')) + ->setNote( + pht( + 'This was a dry run, so no actions were taken.'))); + continue; + } else if (!is_array($log)) { $action_list->addItem( id(new PHUIStatusItemView()) ->setIcon('fa-clock-o', 'grey') diff --git a/src/applications/metamta/constants/PhabricatorMailRoutingRule.php b/src/applications/metamta/constants/PhabricatorMailRoutingRule.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/constants/PhabricatorMailRoutingRule.php @@ -0,0 +1,51 @@ + $strength_v); + } + + public static function getRuleStrength($const) { + $strength = array( + self::ROUTE_AS_NOTIFICATION => 1, + self::ROUTE_AS_MAIL => 2, + ); + + return idx($strength, $const, 0); + } + + public static function getRuleName($const) { + $names = array( + self::ROUTE_AS_NOTIFICATION => pht('Route as Notification'), + self::ROUTE_AS_MAIL => pht('Route as Mail'), + ); + + return idx($names, $const, $const); + } + + public static function getRuleIcon($const) { + $icons = array( + self::ROUTE_AS_NOTIFICATION => 'fa-bell', + self::ROUTE_AS_MAIL => 'fa-envelope', + ); + + return idx($icons, $const, 'fa-question-circle'); + } + + public static function getRuleColor($const) { + $colors = array( + self::ROUTE_AS_NOTIFICATION => 'grey', + self::ROUTE_AS_MAIL => 'grey', + ); + + return idx($colors, $const, 'yellow'); + } + +} 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 @@ -86,6 +86,10 @@ pht('Cc'), $cc_list); + $properties->addProperty( + pht('Sent'), + phabricator_datetime($mail->getDateCreated(), $viewer)); + $properties->addSectionHeader( pht('Message'), PHUIPropertyListView::ICON_SUMMARY); @@ -144,23 +148,16 @@ $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(), + if ($mail->getStatus() == PhabricatorMailOutboundStatus::STATUS_QUEUE) { + $delivery = $this->renderEmptyMessage( pht( 'This message has not been delivered yet, so delivery information '. 'is not available.')); + } else { + $delivery = $this->renderEmptyMessage( + pht( + 'This is an older message that predates recording delivery '. + 'information, so none is available.')); } } else { $actor = idx($actors, $viewer->getPHID()); @@ -214,6 +211,127 @@ $properties->addProperty(pht('Delivery'), $delivery); if ($reasons) { $properties->addProperty(pht('Reasons'), $reasons); + $properties->addProperty( + null, + $this->renderEmptyMessage( + pht( + 'Delivery reasons are listed from weakest to strongest.'))); + } + + $properties->addSectionHeader(pht('Routing Rules')); + + $map = $mail->getDeliveredRoutingMap(); + $routing_detail = null; + if ($map === null) { + if ($mail->getStatus() == PhabricatorMailOutboundStatus::STATUS_QUEUE) { + $routing_result = $this->renderEmptyMessage( + pht( + 'This message has not been sent yet, so routing rules have '. + 'not been computed.')); + } else { + $routing_result = $this->renderEmptyMessage( + pht( + 'This is an older message which predates routing rules.')); + } + } else { + $rule = idx($map, $viewer->getPHID()); + if ($rule === null) { + $rule = idx($map, 'default'); + } + + if ($rule === null) { + $routing_result = $this->renderEmptyMessage( + pht( + 'No routing rules applied when delivering this message to you.')); + } else { + $rule_const = $rule['rule']; + $reason_phid = $rule['reason']; + switch ($rule_const) { + case PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION: + $routing_result = pht( + 'This message was routed as a notification because it '. + 'matched %s.', + $viewer->renderHandle($reason_phid)->render()); + break; + case PhabricatorMailRoutingRule::ROUTE_AS_MAIL: + $routing_result = pht( + 'This message was routed as an email because it matched %s.', + $viewer->renderHandle($reason_phid)->render()); + break; + default: + $routing_result = pht('Unknown routing rule "%s".', $rule_const); + break; + } + } + + $routing_rules = $mail->getDeliveredRoutingRules(); + if ($routing_rules) { + $rules = array(); + foreach ($routing_rules as $rule) { + $phids = idx($rule, 'phids'); + if ($phids === null) { + $rules[] = $rule; + } else if (in_array($viewer->getPHID(), $phids)) { + $rules[] = $rule; + } + } + + // Reorder rules by strength. + foreach ($rules as $key => $rule) { + $const = $rule['routingRule']; + $phids = $rule['phids']; + + if ($phids === null) { + $type = 'A'; + } else { + $type = 'B'; + } + + $rules[$key]['strength'] = sprintf( + '~%s%08d', + $type, + PhabricatorMailRoutingRule::getRuleStrength($const)); + } + $rules = isort($rules, 'strength'); + + $routing_detail = id(new PHUIStatusListView()); + foreach ($rules as $rule) { + $const = $rule['routingRule']; + $phids = $rule['phids']; + + $name = PhabricatorMailRoutingRule::getRuleName($const); + + $icon = PhabricatorMailRoutingRule::getRuleIcon($const); + $color = PhabricatorMailRoutingRule::getRuleColor($const); + + if ($phids === null) { + $kind = pht('Global'); + } else { + $kind = pht('Personal'); + } + + $target = array($kind, ': ', $name); + $target = phutil_tag('strong', array(), $target); + + $item = id(new PHUIStatusItemView()) + ->setTarget($target) + ->setNote($viewer->renderHandle($rule['reasonPHID'])) + ->setIcon($icon, $color); + + $routing_detail->addItem($item); + } + } + } + + $properties->addProperty(pht('Effective Rule'), $routing_result); + + if ($routing_detail !== null) { + $properties->addProperty(pht('All Matching Rules'), $routing_detail); + $properties->addProperty( + null, + $this->renderEmptyMessage( + pht( + 'Matching rules are listed from weakest to strongest.'))); } return $properties; @@ -252,4 +370,8 @@ return $properties; } + private function renderEmptyMessage($message) { + return phutil_tag('em', array(), $message); + } + } diff --git a/src/applications/metamta/herald/PhabricatorMailEmailHeraldField.php b/src/applications/metamta/herald/PhabricatorMailEmailHeraldField.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/herald/PhabricatorMailEmailHeraldField.php @@ -0,0 +1,14 @@ +getSubject(); + } + + protected function getHeraldFieldStandardType() { + return self::STANDARD_TEXT; + } + +} diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php @@ -0,0 +1,62 @@ +mail = $this->newObject(); + } + + protected function newObject() { + return new PhabricatorMetaMTAMail(); + } + + public function getObject() { + return $this->mail; + } + + public function setObject(PhabricatorMetaMTAMail $mail) { + $this->mail = $mail; + return $this; + } + + public function getAdapterContentName() { + return pht('Outbound Mail'); + } + + public function isSingleEventAdapter() { + return true; + } + + public function getRepetitionOptions() { + return array( + HeraldRepetitionPolicyConfig::FIRST, + ); + } + + public function supportsRuleType($rule_type) { + switch ($rule_type) { + case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: + case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: + return true; + case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: + default: + return false; + } + } + + public function getHeraldName() { + return pht('Mail %d', $this->getObject()->getID()); + } + +} diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php @@ -0,0 +1,46 @@ +getAdapter(); + $mail = $adapter->getObject(); + $mail->addRoutingRule($route, $phids, $rule->getPHID()); + + $this->logEffect( + self::DO_ROUTE, + array( + 'route' => $route, + 'phids' => $phids, + )); + } + + protected function getActionEffectMap() { + return array( + self::DO_ROUTE => array( + 'icon' => 'fa-arrow-right', + 'color' => 'green', + 'name' => pht('Routed Message'), + ), + ); + } + + protected function renderActionEffectDescription($type, $data) { + switch ($type) { + case self::DO_ROUTE: + return pht('Routed mail.'); + } + } + +} diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php @@ -0,0 +1,34 @@ +getRule(); + $author_phid = $rule->getAuthorPHID(); + + $this->applyRouting( + $rule, + PhabricatorMailRoutingRule::ROUTE_AS_MAIL, + array($author_phid)); + } + + public function getHeraldActionStandardType() { + return self::STANDARD_NONE; + } + + public function renderActionDescription($value) { + return pht('Deliver as email.'); + } + +} diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php @@ -0,0 +1,34 @@ +getRule(); + $author_phid = $rule->getAuthorPHID(); + + $this->applyRouting( + $rule, + PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION, + array($author_phid)); + } + + public function getHeraldActionStandardType() { + return self::STANDARD_NONE; + } + + public function renderActionDescription($value) { + return pht('Deliver as notification.'); + } + +} diff --git a/src/applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php b/src/applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php --- a/src/applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php +++ b/src/applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php @@ -14,6 +14,10 @@ return false; } + if ($object instanceof PhabricatorMetaMTAMail) { + return false; + } + return true; } diff --git a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php --- a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php +++ b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php @@ -37,7 +37,7 @@ $handle ->setName($name) - ->setFullName($name); + ->setURI('/mail/detail/'.$id.'/'); } } } 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 @@ -18,6 +18,8 @@ const REASON_BOT = 'bot'; const REASON_FORCE = 'force'; const REASON_FORCE_HERALD = 'force-herald'; + const REASON_ROUTE_AS_NOTIFICATION = 'route-as-notification'; + const REASON_ROUTE_AS_MAIL = 'route-as-mail'; private $phid; private $emailAddress; @@ -77,6 +79,7 @@ case self::REASON_NONE: case self::REASON_FORCE: case self::REASON_FORCE_HERALD: + case self::REASON_ROUTE_AS_MAIL: return true; default: // All other reasons cause the message to not be delivered. @@ -99,6 +102,8 @@ self::REASON_UNLOADABLE => pht('Bad Recipient'), self::REASON_FORCE => pht('Forced Mail'), self::REASON_FORCE_HERALD => pht('Forced by Herald'), + self::REASON_ROUTE_AS_NOTIFICATION => pht('Route as Notification'), + self::REASON_ROUTE_AS_MAIL => pht('Route as Mail'), ); return idx($names, $reason, pht('Unknown ("%s")', $reason)); @@ -147,6 +152,12 @@ self::REASON_FORCE_HERALD => pht( 'This recipient was added by a "Send me an Email" rule in Herald, '. 'which overrides some delivery settings.'), + self::REASON_ROUTE_AS_NOTIFICATION => pht( + 'This message was downgraded to a notification by outbound mail '. + 'rules in Herald.'), + self::REASON_ROUTE_AS_MAIL => pht( + 'This message was upgraded to email by outbound mail rules '. + 'in Herald.'), ); return idx($descriptions, $reason, pht('Unknown Reason ("%s")', $reason)); 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 @@ -16,6 +16,7 @@ protected $relatedPHID; private $recipientExpansionMap; + private $routingMap; public function __construct() { @@ -656,6 +657,9 @@ } $this->setParam('actors.sent', $actor_list); + $this->setParam('routing.sent', $this->getParam('routing')); + $this->setParam('routingmap.sent', $this->getRoutingRuleMap()); + if (!$add_to && !$add_cc) { $this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID); $this->setMessage( @@ -963,9 +967,25 @@ } } + foreach ($deliverable as $phid) { + switch ($this->getRoutingRule($phid)) { + case PhabricatorMailRoutingRule::ROUTE_AS_NOTIFICATION: + $actors[$phid]->setUndeliverable( + PhabricatorMetaMTAActor::REASON_ROUTE_AS_NOTIFICATION); + break; + case PhabricatorMailRoutingRule::ROUTE_AS_MAIL: + $actors[$phid]->setDeliverable( + PhabricatorMetaMTAActor::REASON_ROUTE_AS_MAIL); + break; + default: + // No change. + break; + } + } + // If recipients were initially deliverable and were added by "Send me an // email" Herald rules, annotate them as such and make them deliverable - // again, overriding any changes made by the "self mail" and "mail tags" + // again, overriding any changes made by the "self mail" and "mail tags" // settings. $force_recipients = $this->getForceHeraldMailRecipientPHIDs(); $force_recipients = array_fuse($force_recipients); @@ -1065,6 +1085,82 @@ return $this->getParam('actors.sent'); } + public function getDeliveredRoutingRules() { + return $this->getParam('routing.sent'); + } + + public function getDeliveredRoutingMap() { + return $this->getParam('routingmap.sent'); + } + + +/* -( Routing )------------------------------------------------------------ */ + + + public function addRoutingRule($routing_rule, $phids, $reason_phid) { + $routing = $this->getParam('routing', array()); + $routing[] = array( + 'routingRule' => $routing_rule, + 'phids' => $phids, + 'reasonPHID' => $reason_phid, + ); + $this->setParam('routing', $routing); + + // Throw the routing map away so we rebuild it. + $this->routingMap = null; + + return $this; + } + + private function getRoutingRule($phid) { + $map = $this->getRoutingRuleMap(); + + $info = idx($map, $phid, idx($map, 'default')); + if ($info) { + return idx($info, 'rule'); + } + + return null; + } + + private function getRoutingRuleMap() { + if ($this->routingMap === null) { + $map = array(); + + $routing = $this->getParam('routing', array()); + foreach ($routing as $route) { + $phids = $route['phids']; + if ($phids === null) { + $phids = array('default'); + } + + foreach ($phids as $phid) { + $new_rule = $route['routingRule']; + + $current_rule = idx($map, $phid); + if ($current_rule === null) { + $is_stronger = true; + } else { + $is_stronger = PhabricatorMailRoutingRule::isStrongerThan( + $new_rule, + $current_rule); + } + + if ($is_stronger) { + $map[$phid] = array( + 'rule' => $new_rule, + 'reason' => $route['reasonPHID'], + ); + } + } + } + + $this->routingMap = $map; + } + + return $this->routingMap; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -1055,6 +1055,7 @@ } if ($this->shouldPublishFeedStory($object, $xactions)) { + $mailed = array(); foreach ($messages as $mail) { foreach ($mail->buildRecipientList() as $phid) { @@ -2299,6 +2300,8 @@ } } + $this->runHeraldMailRules($messages); + return $messages; } @@ -3140,4 +3143,16 @@ ); } + private function runHeraldMailRules(array $messages) { + foreach ($messages as $message) { + $engine = new HeraldEngine(); + $adapter = id(new PhabricatorMailOutboundMailHeraldAdapter()) + ->setObject($message); + + $rules = $engine->loadRulesForAdapter($adapter); + $effects = $engine->applyRules($rules, $adapter); + $engine->applyEffects($effects, $adapter, $rules); + } + } + }