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

Allow "send me an email" in personal rules to punch through settings

Summary:
Fixes T7731. When a user writes a "Send me an email" rule, always try send them an email, even if their notification settings would normally downgrade it to a notification.

In particular, this is stronger than these downgrades:

- Downgrades due to "self actions";
- downgrades due to "mail tags".

Test Plan:
- Wrote various Herald rules with "Send me an email" rules.
- Used `bin/mail list-outbound` / `show-outbound` to vet generated mail.
- Mail reacted properly to a variety of conditions (disabled accounts, settings, "send me an email" rule, forced delivery).

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7731

Differential Revision: https://secure.phabricator.com/D12300

+83 -66
-6
src/applications/audit/editor/PhabricatorAuditEditor.php
··· 6 6 const MAX_FILES_SHOWN_IN_EMAIL = 1000; 7 7 8 8 private $auditReasonMap = array(); 9 - private $heraldEmailPHIDs = array(); 10 9 private $affectedFiles; 11 10 private $rawPatch; 12 11 ··· 627 626 628 627 protected function getMailTo(PhabricatorLiskDAO $object) { 629 628 $phids = array(); 630 - if ($this->heraldEmailPHIDs) { 631 - $phids = $this->heraldEmailPHIDs; 632 - } 633 629 634 630 if ($object->getAuthorPHID()) { 635 631 $phids[] = $object->getAuthorPHID(); ··· 923 919 $xactions[] = id(new PhabricatorAuditTransaction()) 924 920 ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 925 921 ->setNewValue($add_ccs); 926 - 927 - $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 928 922 929 923 HarbormasterBuildable::applyBuildPlans( 930 924 $object->getPHID(),
-16
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 3 3 final class DifferentialTransactionEditor 4 4 extends PhabricatorApplicationTransactionEditor { 5 5 6 - private $heraldEmailPHIDs; 7 6 private $changedPriorToCommitURI; 8 7 private $isCloseByCommit; 9 8 private $repositoryPHIDOverride = false; ··· 1154 1153 return $phids; 1155 1154 } 1156 1155 1157 - protected function getMailCC(PhabricatorLiskDAO $object) { 1158 - $phids = parent::getMailCC($object); 1159 - 1160 - if ($this->heraldEmailPHIDs) { 1161 - foreach ($this->heraldEmailPHIDs as $phid) { 1162 - $phids[] = $phid; 1163 - } 1164 - } 1165 - 1166 - return $phids; 1167 - } 1168 - 1169 1156 protected function getMailAction( 1170 1157 PhabricatorLiskDAO $object, 1171 1158 array $xactions) { ··· 1729 1716 )); 1730 1717 } 1731 1718 } 1732 - 1733 - // Save extra email PHIDs for later. 1734 - $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 1735 1719 1736 1720 // Apply build plans. 1737 1721 HarbormasterBuildable::applyBuildPlans(
+13
src/applications/herald/adapter/HeraldAdapter.php
··· 110 110 private $customActions = null; 111 111 private $queuedTransactions = array(); 112 112 private $emailPHIDs = array(); 113 + private $forcedEmailPHIDs = array(); 113 114 114 115 public function getEmailPHIDs() { 115 116 return array_values($this->emailPHIDs); 117 + } 118 + 119 + public function getForcedEmailPHIDs() { 120 + return array_values($this->forcedEmailPHIDs); 116 121 } 117 122 118 123 public function getCustomActions() { ··· 1038 1043 1039 1044 foreach ($effect->getTarget() as $phid) { 1040 1045 $this->emailPHIDs[$phid] = $phid; 1046 + 1047 + // If this is a personal rule, we'll force delivery of a real email. This 1048 + // effect is stronger than notification preferences, so you get an actual 1049 + // email even if your preferences are set to "Notify" or "Ignore". 1050 + $rule = $effect->getRule(); 1051 + if ($rule->isPersonalRule()) { 1052 + $this->forcedEmailPHIDs[$phid] = $phid; 1053 + } 1041 1054 } 1042 1055 1043 1056 return new HeraldApplyTranscript(
-17
src/applications/maniphest/editor/ManiphestTransactionEditor.php
··· 3 3 final class ManiphestTransactionEditor 4 4 extends PhabricatorApplicationTransactionEditor { 5 5 6 - private $heraldEmailPHIDs = array(); 7 - 8 6 public function getEditorApplicationClass() { 9 7 return 'PhabricatorManiphestApplication'; 10 8 } ··· 394 392 ); 395 393 } 396 394 397 - protected function getMailCC(PhabricatorLiskDAO $object) { 398 - $phids = array(); 399 - 400 - foreach (parent::getMailCC($object) as $phid) { 401 - $phids[] = $phid; 402 - } 403 - 404 - foreach ($this->heraldEmailPHIDs as $phid) { 405 - $phids[] = $phid; 406 - } 407 - 408 - return $phids; 409 - } 410 - 411 395 public function getMailTagsMap() { 412 396 return array( 413 397 ManiphestTransaction::MAILTAG_STATUS => ··· 521 505 HeraldAdapter $adapter, 522 506 HeraldTranscript $transcript) { 523 507 524 - $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 525 508 $xactions = array(); 526 509 527 510 $cc_phids = $adapter->getCcPHIDs();
+4
src/applications/metamta/query/PhabricatorMetaMTAActor.php
··· 16 16 const REASON_MAILTAGS = 'mailtags'; 17 17 const REASON_BOT = 'bot'; 18 18 const REASON_FORCE = 'force'; 19 + const REASON_FORCE_HERALD = 'force-herald'; 19 20 20 21 private $phid; 21 22 private $emailAddress; ··· 108 109 'Mail which uses forced delivery is usually related to account '. 109 110 'management or authentication. For example, password reset email '. 110 111 'ignores mail preferences.'), 112 + self::REASON_FORCE_HERALD => pht( 113 + 'This recipient was added by a "Send me an Email" rule in Herald, '. 114 + 'which overrides some delivery settings.'), 111 115 ); 112 116 113 117 return idx($descriptions, $reason, pht('Unknown Reason ("%s")', $reason));
+56 -12
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 141 141 return $this->getParam('exclude', array()); 142 142 } 143 143 144 + public function setForceHeraldMailRecipientPHIDs(array $force) { 145 + $this->setParam('herald-force-recipients', $force); 146 + return $this; 147 + } 148 + 149 + private function getForceHeraldMailRecipientPHIDs() { 150 + return $this->getParam('herald-force-recipients', array()); 151 + } 152 + 144 153 public function getTranslation(array $objects) { 145 154 $default_translation = PhabricatorEnv::getEnvConfig('translation.provider'); 146 155 $return = null; ··· 851 860 } 852 861 853 862 if ($this->getForceDelivery()) { 854 - // If we're forcing delivery, skip all the opt-out checks. 863 + // If we're forcing delivery, skip all the opt-out checks. We don't 864 + // bother annotating reasoning on the mail in this case because it should 865 + // always be obvious why the mail hit this rule (e.g., it is a password 866 + // reset mail). 855 867 foreach ($actors as $actor) { 856 868 $actor->setDeliverable(PhabricatorMetaMTAActor::REASON_FORCE); 857 869 } ··· 867 879 $actor->setUndeliverable(PhabricatorMetaMTAActor::REASON_RESPONSE); 868 880 } 869 881 882 + // Before running more rules, save a list of the actors who were 883 + // deliverable before we started running preference-based rules. This stops 884 + // us from trying to send mail to disabled users just because a Herald rule 885 + // added them, for example. 886 + $deliverable = array(); 887 + foreach ($actors as $phid => $actor) { 888 + if ($actor->isDeliverable()) { 889 + $deliverable[] = $phid; 890 + } 891 + } 892 + 893 + // For the rest of the rules, order matters. We're going to run all the 894 + // possible rules in order from weakest to strongest, and let the strongest 895 + // matching rule win. The weaker rules leave annotations behind which help 896 + // users understand why the mail was routed the way it was. 897 + 870 898 // Exclude the actor if their preferences are set. 871 899 $from_phid = $this->getParam('from'); 872 900 $from_actor = idx($actors, $from_phid); ··· 892 920 $actor_phids); 893 921 $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 894 922 895 - // Exclude recipients who don't want any mail. 896 - foreach ($all_prefs as $phid => $prefs) { 897 - $exclude = $prefs->getPreference( 898 - PhabricatorUserPreferences::PREFERENCE_NO_MAIL, 899 - false); 900 - if ($exclude) { 901 - $actors[$phid]->setUndeliverable( 902 - PhabricatorMetaMTAActor::REASON_MAIL_DISABLED); 903 - } 904 - } 905 - 906 923 $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; 907 924 908 925 // Exclude all recipients who have set preferences to not receive this type ··· 929 946 $actors[$phid]->setUndeliverable( 930 947 PhabricatorMetaMTAActor::REASON_MAILTAGS); 931 948 } 949 + } 950 + } 951 + 952 + // If recipients were initially deliverable and were added by "Send me an 953 + // email" Herald rules, annotate them as such and make them deliverable 954 + // again, overriding any changes made by the "self mail" and "mail tags" 955 + // settings. 956 + $force_recipients = $this->getForceHeraldMailRecipientPHIDs(); 957 + $force_recipients = array_fuse($force_recipients); 958 + if ($force_recipients) { 959 + foreach ($deliverable as $phid) { 960 + if (isset($force_recipients[$phid])) { 961 + $actors[$phid]->setDeliverable( 962 + PhabricatorMetaMTAActor::REASON_FORCE_HERALD); 963 + } 964 + } 965 + } 966 + 967 + // Exclude recipients who don't want any mail. This rule is very strong 968 + // and runs last. 969 + foreach ($all_prefs as $phid => $prefs) { 970 + $exclude = $prefs->getPreference( 971 + PhabricatorUserPreferences::PREFERENCE_NO_MAIL, 972 + false); 973 + if ($exclude) { 974 + $actors[$phid]->setUndeliverable( 975 + PhabricatorMetaMTAActor::REASON_MAIL_DISABLED); 932 976 } 933 977 } 934 978
-13
src/applications/phriction/editor/PhrictionTransactionEditor.php
··· 13 13 private $skipAncestorCheck; 14 14 private $contentVersion; 15 15 private $processContentVersionError = true; 16 - private $heraldEmailPHIDs = array(); 17 16 18 17 public function setDescription($description) { 19 18 $this->description = $description; ··· 367 366 $object->getContent()->getAuthorPHID(), 368 367 $this->getActingAsPHID(), 369 368 ); 370 - } 371 - 372 - protected function getMailCC(PhabricatorLiskDAO $object) { 373 - $phids = array(); 374 - 375 - foreach ($this->heraldEmailPHIDs as $phid) { 376 - $phids[] = $phid; 377 - } 378 - 379 - return $phids; 380 369 } 381 370 382 371 public function getMailTagsMap() { ··· 800 789 ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 801 790 ->setNewValue(array('+' => $value)); 802 791 } 803 - 804 - $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 805 792 806 793 return $xactions; 807 794 }
+10 -2
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1967 1967 return; 1968 1968 } 1969 1969 1970 - $email_to = array_filter(array_unique($this->getMailTo($object))); 1971 - $email_cc = array_filter(array_unique($this->getMailCC($object))); 1970 + $email_force = array(); 1971 + $email_to = $this->getMailTo($object); 1972 + $email_cc = $this->getMailCC($object); 1973 + 1974 + $adapter = $this->getHeraldAdapter(); 1975 + if ($adapter) { 1976 + $email_cc = array_merge($email_cc, $adapter->getEmailPHIDs()); 1977 + $email_force = $adapter->getForcedEmailPHIDs(); 1978 + } 1972 1979 1973 1980 $phids = array_merge($email_to, $email_cc); 1974 1981 $handles = id(new PhabricatorHandleQuery()) ··· 1993 2000 ->setThreadID($this->getMailThreadID($object), $this->getIsNewObject()) 1994 2001 ->setRelatedPHID($object->getPHID()) 1995 2002 ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 2003 + ->setForceHeraldMailRecipientPHIDs($email_force) 1996 2004 ->setMailTags($mail_tags) 1997 2005 ->setIsBulk(true) 1998 2006 ->setBody($body->render())