Differential D11329 Diff 27431 src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
| Show All 23 Lines | abstract class PhabricatorApplicationTransactionEditor | ||||
| private $unmentionablePHIDMap = array(); | private $unmentionablePHIDMap = array(); | ||||
| private $isPreview; | private $isPreview; | ||||
| private $isHeraldEditor; | private $isHeraldEditor; | ||||
| private $isInverseEdgeEditor; | private $isInverseEdgeEditor; | ||||
| private $actingAsPHID; | private $actingAsPHID; | ||||
| private $disableEmail; | private $disableEmail; | ||||
| /** | /** | ||||
| * Get the class name for the application this editor is a part of. | * Get the class name for the application this editor is a part of. | ||||
| * | * | ||||
| * Uninstalling the application will disable the editor. | * Uninstalling the application will disable the editor. | ||||
| * | * | ||||
| * @return string Editor's application class name. | * @return string Editor's application class name. | ||||
| */ | */ | ||||
| abstract public function getEditorApplicationClass(); | abstract public function getEditorApplicationClass(); | ||||
| ▲ Show 20 Lines • Show All 745 Lines • ▼ Show 20 Lines | final public function applyTransactions( | ||||
| // Before sending mail or publishing feed stories, reload the object | // Before sending mail or publishing feed stories, reload the object | ||||
| // subscribers to pick up changes caused by Herald (or by other side effects | // subscribers to pick up changes caused by Herald (or by other side effects | ||||
| // in various transaction phases). | // in various transaction phases). | ||||
| $this->loadSubscribers($object); | $this->loadSubscribers($object); | ||||
| $this->loadHandles($xactions); | $this->loadHandles($xactions); | ||||
| $notifications_sent = $this->sendNotifications($object, $xactions); | |||||
| // Fall back to the old code if no notification worker is in use | |||||
| if (!$notifications_sent) { | |||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| $mail = null; | $mail = null; | ||||
| if (!$this->getDisableEmail()) { | if (!$this->getDisableEmail()) { | ||||
| if ($this->shouldSendMail($object, $xactions)) { | if ($this->shouldSendMail($object, $xactions)) { | ||||
| $mail = $this->sendMail($object, $xactions); | $mail = $this->sendMail($object, $xactions); | ||||
| } | } | ||||
| } | } | ||||
| if ($this->supportsSearch()) { | if ($this->supportsSearch()) { | ||||
| id(new PhabricatorSearchIndexer()) | id(new PhabricatorSearchIndexer()) | ||||
| ->queueDocumentForIndexing( | ->queueDocumentForIndexing( | ||||
| $object->getPHID(), | $object->getPHID(), | ||||
| $this->getSearchContextParameter($object, $xactions)); | $this->getSearchContextParameter($object, $xactions)); | ||||
| } | } | ||||
| if ($this->shouldPublishFeedStory($object, $xactions)) { | if ($this->shouldPublishFeedStory($object, $xactions)) { | ||||
| $mailed = array(); | $mailed = array(); | ||||
| if ($mail) { | if ($mail) { | ||||
| $mailed = $mail->buildRecipientList(); | $mailed = $mail->buildRecipientList(); | ||||
| } | } | ||||
| $this->publishFeedStory( | $this->publishFeedStory( | ||||
| $object, | $object, | ||||
| $xactions, | $xactions, | ||||
| $mailed); | $mailed); | ||||
| } | } | ||||
| } | |||||
| $this->didApplyTransactions($xactions); | $this->didApplyTransactions($xactions); | ||||
| if ($object instanceof PhabricatorCustomFieldInterface) { | if ($object instanceof PhabricatorCustomFieldInterface) { | ||||
| // Maybe this makes more sense to move into the search index itself? For | // Maybe this makes more sense to move into the search index itself? For | ||||
| // now I'm putting it here since I think we might end up with things that | // now I'm putting it here since I think we might end up with things that | ||||
| // need it to be up to date once the next page loads, but if we don't go | // need it to be up to date once the next page loads, but if we don't go | ||||
| // there we we could move it into search once search moves to the daemons. | // there we we could move it into search once search moves to the daemons. | ||||
| ▲ Show 20 Lines • Show All 1,062 Lines • ▼ Show 20 Lines | /* -( Implicit CCs )------------------------------------------------------- */ | ||||
| protected function shouldImplyCC( | protected function shouldImplyCC( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| PhabricatorApplicationTransaction $xaction) { | PhabricatorApplicationTransaction $xaction) { | ||||
| return $xaction->isCommentTransaction(); | return $xaction->isCommentTransaction(); | ||||
| } | } | ||||
| /* -( Notification Worker (Mail & Feeds) )----------------------------------- */ | |||||
| protected function getTransactionNotificationWorkerClass() { | |||||
| throw new Exception('This function needs to be implemented to be able to render Email'); | |||||
| } | |||||
| protected function sendNotifications( | |||||
| PhabricatorLiskDAO $object, | |||||
| array $xactions) { | |||||
| try { | |||||
| $worker_class = $this->getTransactionNotificationWorkerClass(); | |||||
| } catch (Exception $e) { | |||||
| return false; | |||||
| } | |||||
| $herald_xscript = $this->getHeraldTranscript(); | |||||
| if ($herald_xscript) { | |||||
| $herald_header = $herald_xscript->getXHeraldRulesHeader(); | |||||
| $herald_header = HeraldTranscript::saveXHeraldRulesHeader( | |||||
| $object->getPHID(), | |||||
| $herald_header); | |||||
| } else { | |||||
| $herald_header = HeraldTranscript::loadXHeraldRulesHeader( | |||||
| $object->getPHID()); | |||||
| } | |||||
epriestley: This is a good catch, I missed it! | |||||
| PhabricatorWorker::scheduleTask( | |||||
| $worker_class, | |||||
| array( | |||||
| 'object' => $object->getPHID(), | |||||
| 'xactions' => mpull($xactions, 'getPHID'), | |||||
| 'shouldSendMail' => $this->shouldSendMail($object, $xactions), | |||||
| 'shouldPublishFeedStory' => $this->shouldPublishFeedStory($object, $xactions), | |||||
| 'mailTo' => $this->getMailTo($object), | |||||
| 'mailCC' => $this->getMailCC($object), | |||||
| 'actingAsPHID' => $this->getActingAsPHID(), | |||||
| 'isNewObject' => $this->getIsNewObject(), | |||||
| 'heraldHeader' => $herald_header, | |||||
| 'excludeMailRecipientPHIDs' => $this->getExcludeMailRecipientPHIDs(), | |||||
| 'parentMessageID' => $this->getParentMessageID(), | |||||
| ), | |||||
Not Done Inline ActionsThis one too. epriestley: This one too. | |||||
| array( | |||||
| 'priority' => PhabricatorWorker::PRIORITY_ALERTS, // FIXME: right prio? | |||||
| )); | |||||
| return true; | |||||
| } | |||||
| /* -( Sending Mail )------------------------------------------------------- */ | /* -( Sending Mail )------------------------------------------------------- */ | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| protected function shouldSendMail( | protected function shouldSendMail( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function sendMail( | protected function sendMail( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| // Check if any of the transactions are visible. If we don't have any | // Check if any of the transactions are visible. If we don't have any | ||||
| // visible transactions, don't send the mail. | // visible transactions, don't send the mail. | ||||
| $any_visible = false; | $any_visible = false; | ||||
| foreach ($xactions as $xaction) { | foreach ($xactions as $xaction) { | ||||
| if (!$xaction->shouldHideForMail($xactions)) { | if (!$xaction->shouldHideForMail($xactions)) { | ||||
| $any_visible = true; | $any_visible = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (!$any_visible) { | if (!$any_visible) { | ||||
| return; | return; | ||||
| } | } | ||||
| $email_to = array_filter(array_unique($this->getMailTo($object))); | $email_to = array_filter(array_unique($this->getMailTo($object))); | ||||
| $email_cc = array_filter(array_unique($this->getMailCC($object))); | $email_cc = array_filter(array_unique($this->getMailCC($object))); | ||||
| $phids = array_merge($email_to, $email_cc); | $phids = array_merge($email_to, $email_cc); | ||||
| $handles = id(new PhabricatorHandleQuery()) | $handles = id(new PhabricatorHandleQuery()) | ||||
| ->setViewer($this->requireActor()) | ->setViewer($this->requireActor()) | ||||
| ->withPHIDs($phids) | ->withPHIDs($phids) | ||||
| ->execute(); | ->execute(); | ||||
| $template = $this->buildMailTemplate($object); | $template = $this->buildMailTemplate($object); | ||||
| $body = $this->buildMailBody($object, $xactions); | $body = $this->buildMailBody($object, $xactions); | ||||
| ▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | protected function sendMail( | ||||
| } | } | ||||
| $template->addTos($email_to); | $template->addTos($email_to); | ||||
| $template->addCCs($email_cc); | $template->addCCs($email_cc); | ||||
| return $template; | return $template; | ||||
| } | } | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| private function addMailProjectMetadata( | private function addMailProjectMetadata( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| PhabricatorMetaMTAMail $template) { | PhabricatorMetaMTAMail $template) { | ||||
| $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( | $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( | ||||
| $object->getPHID(), | $object->getPHID(), | ||||
| PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); | PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); | ||||
| Show All 22 Lines | if (!$project_tags) { | ||||
| return; | return; | ||||
| } | } | ||||
| $project_tags = implode(', ', $project_tags); | $project_tags = implode(', ', $project_tags); | ||||
| $template->addHeader('X-Phabricator-Projects', $project_tags); | $template->addHeader('X-Phabricator-Projects', $project_tags); | ||||
| } | } | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getMailThreadID(PhabricatorLiskDAO $object) { | protected function getMailThreadID(PhabricatorLiskDAO $object) { | ||||
| return $object->getPHID(); | return $object->getPHID(); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getStrongestAction( | protected function getStrongestAction( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| return last(msort($xactions, 'getActionStrength')); | return last(msort($xactions, 'getActionStrength')); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function buildReplyHandler(PhabricatorLiskDAO $object) { | protected function buildReplyHandler(PhabricatorLiskDAO $object) { | ||||
| throw new Exception('Capability not supported.'); | throw new Exception('Capability not supported.'); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getMailSubjectPrefix() { | protected function getMailSubjectPrefix() { | ||||
| throw new Exception('Capability not supported.'); | throw new Exception('Capability not supported.'); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // FIXME: Move it to a ManiphestTransaction? | |||||
| // This is currently duplicated in ManiphestTransactionNotificationWorker! | |||||
| // And also needed in email preferences. TODO: Find a place to put this stuff | |||||
| protected function getMailTags( | protected function getMailTags( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| $tags = array(); | $tags = array(); | ||||
| foreach ($xactions as $xaction) { | foreach ($xactions as $xaction) { | ||||
| $tags[] = $xaction->getMailTags(); | $tags[] = $xaction->getMailTags(); | ||||
| } | } | ||||
| return array_mergev($tags); | return array_mergev($tags); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| public function getMailTagsMap() { | public function getMailTagsMap() { | ||||
| // TODO: We should move shared mail tags, like "comment", here. | // TODO: We should move shared mail tags, like "comment", here. | ||||
| return array(); | return array(); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getMailAction( | protected function getMailAction( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| return $this->getStrongestAction($object, $xactions)->getActionName(); | return $this->getStrongestAction($object, $xactions)->getActionName(); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function buildMailTemplate(PhabricatorLiskDAO $object) { | protected function buildMailTemplate(PhabricatorLiskDAO $object) { | ||||
| throw new Exception('Capability not supported.'); | throw new Exception('Capability not supported.'); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | protected function getMailCC(PhabricatorLiskDAO $object) { | ||||
| return array_mergev($phids); | return array_mergev($phids); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task mail | * @task mail | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function buildMailBody( | protected function buildMailBody( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| $body = new PhabricatorMetaMTAMailBody(); | $body = new PhabricatorMetaMTAMailBody(); | ||||
| $body->setViewer($this->requireActor()); | $body->setViewer($this->requireActor()); | ||||
| $this->addHeadersAndCommentsToMailBody($body, $xactions); | $this->addHeadersAndCommentsToMailBody($body, $xactions); | ||||
| ▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | protected function shouldPublishFeedStory( | ||||
| array $xactions) { | array $xactions) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /** | /** | ||||
| * @task feed | * @task feed | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getFeedStoryType() { | protected function getFeedStoryType() { | ||||
| return 'PhabricatorApplicationTransactionFeedStory'; | return 'PhabricatorApplicationTransactionFeedStory'; | ||||
| } | } | ||||
| /** | /** | ||||
| * @task feed | * @task feed | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getFeedRelatedPHIDs( | protected function getFeedRelatedPHIDs( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| $phids = array( | $phids = array( | ||||
| $object->getPHID(), | $object->getPHID(), | ||||
| $this->getActingAsPHID(), | $this->getActingAsPHID(), | ||||
| ); | ); | ||||
| Show All 9 Lines | protected function getFeedRelatedPHIDs( | ||||
| return $phids; | return $phids; | ||||
| } | } | ||||
| /** | /** | ||||
| * @task feed | * @task feed | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getFeedNotifyPHIDs( | protected function getFeedNotifyPHIDs( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| return array_unique(array_merge( | return array_unique(array_merge( | ||||
| $this->getMailTo($object), | $this->getMailTo($object), | ||||
| $this->getMailCC($object))); | $this->getMailCC($object))); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task feed | * @task feed | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function getFeedStoryData( | protected function getFeedStoryData( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| $xactions = msort($xactions, 'getActionStrength'); | $xactions = msort($xactions, 'getActionStrength'); | ||||
| $xactions = array_reverse($xactions); | $xactions = array_reverse($xactions); | ||||
| return array( | return array( | ||||
| 'objectPHID' => $object->getPHID(), | 'objectPHID' => $object->getPHID(), | ||||
| 'transactionPHIDs' => mpull($xactions, 'getPHID'), | 'transactionPHIDs' => mpull($xactions, 'getPHID'), | ||||
| ); | ); | ||||
| } | } | ||||
| /** | /** | ||||
| * @task feed | * @task feed | ||||
| */ | */ | ||||
| // NotificationWorkerGrepToken | |||||
| // Remove this code when all apps have transitioned | |||||
| protected function publishFeedStory( | protected function publishFeedStory( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions, | array $xactions, | ||||
| array $mailed_phids) { | array $mailed_phids) { | ||||
| $xactions = mfilter($xactions, 'shouldHideForFeed', true); | $xactions = mfilter($xactions, 'shouldHideForFeed', true); | ||||
| if (!$xactions) { | if (!$xactions) { | ||||
| ▲ Show 20 Lines • Show All 277 Lines • Show Last 20 Lines | |||||
This is a good catch, I missed it!