diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 4d143d0d44..108c73b044 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1356,6 +1356,7 @@ phutil_register_library_map(array( 'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', 'HeraldContentSourceField' => 'applications/herald/field/HeraldContentSourceField.php', 'HeraldController' => 'applications/herald/controller/HeraldController.php', + 'HeraldCoreStateReasons' => 'applications/herald/state/HeraldCoreStateReasons.php', 'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php', 'HeraldDifferentialAdapter' => 'applications/differential/herald/HeraldDifferentialAdapter.php', 'HeraldDifferentialDiffAdapter' => 'applications/differential/herald/HeraldDifferentialDiffAdapter.php', @@ -6529,6 +6530,7 @@ phutil_register_library_map(array( 'HeraldConditionTranscript' => 'Phobject', 'HeraldContentSourceField' => 'HeraldField', 'HeraldController' => 'PhabricatorController', + 'HeraldCoreStateReasons' => 'HeraldStateReasons', 'HeraldDAO' => 'PhabricatorLiskDAO', 'HeraldDifferentialAdapter' => 'HeraldAdapter', 'HeraldDifferentialDiffAdapter' => 'HeraldDifferentialAdapter', diff --git a/src/applications/herald/state/HeraldCoreStateReasons.php b/src/applications/herald/state/HeraldCoreStateReasons.php new file mode 100644 index 0000000000..7a033ff51e --- /dev/null +++ b/src/applications/herald/state/HeraldCoreStateReasons.php @@ -0,0 +1,18 @@ + pht( + 'This change applied silently, so mail and other notifications '. + 'will not be sent.'), + ); + + return idx($reasons, $reason); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index b4ad4365a7..829e3ab43d 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -68,7 +68,9 @@ abstract class PhabricatorApplicationTransactionEditor private $feedNotifyPHIDs = array(); private $feedRelatedPHIDs = array(); private $feedShouldPublish = false; + private $mailShouldSend = false; private $modularTypes; + private $silent; private $transactionQueue = array(); @@ -187,6 +189,15 @@ abstract class PhabricatorApplicationTransactionEditor return $this->isPreview; } + public function setIsSilent($silent) { + $this->silent = $silent; + return $this; + } + + public function getIsSilent() { + return $this->silent; + } + public function setIsInverseEdgeEditor($is_inverse_edge_editor) { $this->isInverseEdgeEditor = $is_inverse_edge_editor; return $this; @@ -790,6 +801,10 @@ abstract class PhabricatorApplicationTransactionEditor $xaction->setObjectPHID($object->getPHID()); } + if ($this->getIsSilent()) { + $xaction->setIsSilentTransaction(true); + } + return $xaction; } @@ -1136,15 +1151,22 @@ abstract class PhabricatorApplicationTransactionEditor // Editors need to pass into workers. $object = $this->willPublish($object, $xactions); - if ($this->shouldSendMail($object, $xactions)) { - $this->mailToPHIDs = $this->getMailTo($object); - $this->mailCCPHIDs = $this->getMailCC($object); - } + if (!$this->getIsSilent()) { + if ($this->shouldSendMail($object, $xactions)) { + $this->mailShouldSend = true; + $this->mailToPHIDs = $this->getMailTo($object); + $this->mailCCPHIDs = $this->getMailCC($object); + } - if ($this->shouldPublishFeedStory($object, $xactions)) { - $this->feedShouldPublish = true; - $this->feedRelatedPHIDs = $this->getFeedRelatedPHIDs($object, $xactions); - $this->feedNotifyPHIDs = $this->getFeedNotifyPHIDs($object, $xactions); + if ($this->shouldPublishFeedStory($object, $xactions)) { + $this->feedShouldPublish = true; + $this->feedRelatedPHIDs = $this->getFeedRelatedPHIDs( + $object, + $xactions); + $this->feedNotifyPHIDs = $this->getFeedNotifyPHIDs( + $object, + $xactions); + } } PhabricatorWorker::scheduleTask( @@ -1186,7 +1208,7 @@ abstract class PhabricatorApplicationTransactionEditor $this->object = $object; $messages = array(); - if ($this->shouldSendMail($object, $xactions)) { + if ($this->mailShouldSend) { $messages = $this->buildMail($object, $xactions); } @@ -3138,6 +3160,16 @@ abstract class PhabricatorApplicationTransactionEditor $adapter->setApplicationEmail($this->getApplicationEmail()); } + // If this editor is operating in silent mode, tell Herald that we aren't + // going to send any mail. This allows it to skip "the first time this + // rule matches, send me an email" rules which would otherwise match even + // though we aren't going to send any mail. + if ($this->getIsSilent()) { + $adapter->setForbiddenAction( + HeraldMailableState::STATECONST, + HeraldCoreStateReasons::REASON_SILENT); + } + $xscript = HeraldEngine::loadAndApplyRules($adapter); $this->setHeraldAdapter($adapter); @@ -3493,6 +3525,7 @@ abstract class PhabricatorApplicationTransactionEditor 'feedNotifyPHIDs', 'feedRelatedPHIDs', 'feedShouldPublish', + 'mailShouldSend', ); } @@ -3875,7 +3908,8 @@ abstract class PhabricatorApplicationTransactionEditor ->setActor($this->getActor()) ->setContentSource($this->getContentSource()) ->setContinueOnNoEffect($this->getContinueOnNoEffect()) - ->setContinueOnMissingFields($this->getContinueOnMissingFields()); + ->setContinueOnMissingFields($this->getContinueOnMissingFields()) + ->setIsSilent($this->getIsSilent()); if ($this->actingAsPHID !== null) { $editor->setActingAsPHID($this->actingAsPHID); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index bf3c616df9..d49c3bf675 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -158,6 +158,14 @@ abstract class PhabricatorApplicationTransaction return (bool)$this->getMetadataValue('core.default', false); } + public function setIsSilentTransaction($silent) { + return $this->setMetadataValue('core.silent', $silent); + } + + public function getIsSilentTransaction() { + return (bool)$this->getMetadataValue('core.silent', false); + } + public function attachComment( PhabricatorApplicationTransactionComment $comment) { $this->comment = $comment; @@ -1515,6 +1523,12 @@ abstract class PhabricatorApplicationTransaction if ($apart > (60 * 2)) { return false; } + + // Don't group silent and nonsilent transactions together. + $is_silent = $this->getIsSilentTransaction(); + if ($is_silent != $xaction->getIsSilentTransaction()) { + return false; + } } return true; diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index 0d427419d5..e10f5c008e 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -423,7 +423,8 @@ class PhabricatorApplicationTransactionView extends AphrontView { ->setUserHandle($xaction->getHandle($xaction->getAuthorPHID())) ->setIcon($xaction->getIcon()) ->setColor($xaction->getColor()) - ->setHideCommentOptions($this->getHideCommentOptions()); + ->setHideCommentOptions($this->getHideCommentOptions()) + ->setIsSilent($xaction->getIsSilentTransaction()); list($token, $token_removed) = $xaction->getToken(); if ($token) { diff --git a/src/view/phui/PHUITimelineEventView.php b/src/view/phui/PHUITimelineEventView.php index 51a0ea5aae..f07b40cb39 100644 --- a/src/view/phui/PHUITimelineEventView.php +++ b/src/view/phui/PHUITimelineEventView.php @@ -29,6 +29,7 @@ final class PHUITimelineEventView extends AphrontView { private $authorPHID; private $badges = array(); private $pinboardItems = array(); + private $isSilent; public function setAuthorPHID($author_phid) { $this->authorPHID = $author_phid; @@ -177,6 +178,15 @@ final class PHUITimelineEventView extends AphrontView { return $this; } + public function setIsSilent($is_silent) { + $this->isSilent = $is_silent; + return $this; + } + + public function getIsSilent() { + return $this->isSilent; + } + public function setReallyMajorEvent($me) { $this->reallyMajorEvent = $me; return $this;