public function testRecipients()
 {
     $user = $this->generateNewTestUser();
     $phid = $user->getPHID();
     $mailer = new PhabricatorMailImplementationTestAdapter();
     $mail = new PhabricatorMetaMTAMail();
     $mail->addTos(array($phid));
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"To" is a recipient.'));
     // Test that the "No Self Mail" and "No Mail" preferences work correctly.
     $mail->setFrom($phid);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     $user = $this->writeSetting($user, PhabricatorEmailSelfActionsSetting::SETTINGKEY, true);
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('"From" excludes recipients with no-self-mail set.'));
     $user = $this->writeSetting($user, PhabricatorEmailSelfActionsSetting::SETTINGKEY, null);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     $user = $this->writeSetting($user, PhabricatorEmailNotificationsSetting::SETTINGKEY, true);
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('"From" excludes recipients with no-mail set.'));
     $mail->setForceDelivery(true);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" includes no-mail recipients when forced.'));
     $mail->setForceDelivery(false);
     $user = $this->writeSetting($user, PhabricatorEmailNotificationsSetting::SETTINGKEY, null);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     // Test that explicit exclusion works correctly.
     $mail->setExcludeMailRecipientPHIDs(array($phid));
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('Explicit exclude excludes recipients.'));
     $mail->setExcludeMailRecipientPHIDs(array());
     // Test that mail tag preferences exclude recipients.
     $user = $this->writeSetting($user, PhabricatorEmailTagsSetting::SETTINGKEY, array('test-tag' => false));
     $mail->setMailTags(array('test-tag'));
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('Tag preference excludes recipients.'));
     $user = $this->writeSetting($user, PhabricatorEmailTagsSetting::SETTINGKEY, null);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), 'Recipients restored after tag preference removed.');
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if ($request->isFormPost()) {
         $mail = new PhabricatorMetaMTAMail();
         $mail->addTos($request->getArr('to'));
         $mail->addCCs($request->getArr('cc'));
         $mail->setSubject($request->getStr('subject'));
         $mail->setBody($request->getStr('body'));
         $files = $request->getArr('files');
         if ($files) {
             foreach ($files as $phid) {
                 $file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $phid);
                 $mail->addAttachment(new PhabricatorMetaMTAAttachment($file->loadFileData(), $file->getName(), $file->getMimeType()));
             }
         }
         $mail->setFrom($request->getUser()->getPHID());
         $mail->setSimulatedFailureCount($request->getInt('failures'));
         $mail->setIsHTML($request->getInt('html'));
         $mail->setIsBulk($request->getInt('bulk'));
         $mail->setMailTags($request->getStrList('mailtags'));
         $mail->save();
         if ($request->getInt('immediately')) {
             $mail->sendNow();
         }
         return id(new AphrontRedirectResponse())->setURI($this->getApplicationURI('/view/' . $mail->getID() . '/'));
     }
     $failure_caption = "Enter a number to simulate that many consecutive send failures before " . "really attempting to deliver via the underlying MTA.";
     $doclink_href = PhabricatorEnv::getDoclink('article/Configuring_Outbound_Email.html');
     $doclink = phutil_render_tag('a', array('href' => $doclink_href, 'target' => '_blank'), 'Configuring Outbound Email');
     $instructions = '<p class="aphront-form-instructions">This form will send a normal ' . 'email using the settings you have configured for Phabricator. For more ' . 'information, see ' . $doclink . '.</p>';
     $adapter = PhabricatorEnv::getEnvConfig('metamta.mail-adapter');
     $warning = null;
     if ($adapter == 'PhabricatorMailImplementationTestAdapter') {
         $warning = new AphrontErrorView();
         $warning->setTitle('Email is Disabled');
         $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
         $warning->appendChild('<p>This installation of Phabricator is currently set to use ' . '<tt>PhabricatorMailImplementationTestAdapter</tt> to deliver ' . 'outbound email. This completely disables outbound email! All ' . 'outbound email will be thrown in a deep, dark hole until you ' . 'configure a real adapter.</p>');
     }
     $panel_id = celerity_generate_unique_node_id();
     $phdlink_href = PhabricatorEnv::getDoclink('article/Managing_Daemons_with_phd.html');
     $phdlink = phutil_render_tag('a', array('href' => $phdlink_href, 'target' => '_blank'), '"phd start"');
     $form = new AphrontFormView();
     $form->setUser($request->getUser());
     $form->appendChild($instructions)->appendChild(id(new AphrontFormStaticControl())->setLabel('Adapter')->setValue($adapter))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('To')->setName('to')->setDatasource('/typeahead/common/mailable/'))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('CC')->setName('cc')->setDatasource('/typeahead/common/mailable/'))->appendChild(id(new AphrontFormTextControl())->setLabel('Subject')->setName('subject'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Body')->setName('body'))->appendChild(id(new AphrontFormTextControl())->setLabel('Mail Tags')->setName('mailtags')->setCaption('Example: <tt>differential-cc, differential-comment</tt>'))->appendChild(id(new AphrontFormDragAndDropUploadControl())->setLabel('Attach Files')->setName('files')->setDragAndDropTarget($panel_id)->setActivatedClass('aphront-panel-view-drag-and-drop'))->appendChild(id(new AphrontFormTextControl())->setLabel('Simulate Failures')->setName('failures')->setCaption($failure_caption))->appendChild(id(new AphrontFormCheckboxControl())->setLabel('HTML')->addCheckbox('html', '1', 'Send as HTML email.'))->appendChild(id(new AphrontFormCheckboxControl())->setLabel('Bulk')->addCheckbox('bulk', '1', 'Send with bulk email headers.'))->appendChild(id(new AphrontFormCheckboxControl())->setLabel('Send Now')->addCheckbox('immediately', '1', 'Send immediately. (Do not enqueue for daemons.)', PhabricatorEnv::getEnvConfig('metamta.send-immediately'))->setCaption('Daemons can be started with ' . $phdlink . '.'))->appendChild(id(new AphrontFormSubmitControl())->setValue('Send Mail'));
     $panel = new AphrontPanelView();
     $panel->setHeader('Send Email');
     $panel->appendChild($form);
     $panel->setID($panel_id);
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     $nav = $this->buildSideNavView();
     $nav->selectFilter('send');
     $nav->appendChild(array($warning, $panel));
     return $this->buildApplicationPage($nav, array('title' => 'Send Test'));
 }
 public function testRecipients()
 {
     $user = $this->generateNewTestUser();
     $phid = $user->getPHID();
     $prefs = $user->loadPreferences();
     $mailer = new PhabricatorMailImplementationTestAdapter();
     $mail = new PhabricatorMetaMTAMail();
     $mail->addTos(array($phid));
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"To" is a recipient.'));
     // Test that the "No Self Mail" and "No Mail" preferences work correctly.
     $mail->setFrom($phid);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     $prefs->setPreference(PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL, true);
     $prefs->save();
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('"From" excludes recipients with no-self-mail set.'));
     $prefs->unsetPreference(PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL);
     $prefs->save();
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     $prefs->setPreference(PhabricatorUserPreferences::PREFERENCE_NO_MAIL, true);
     $prefs->save();
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('"From" excludes recipients with no-mail set.'));
     $mail->setForceDelivery(true);
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" includes no-mail recipients when forced.'));
     $mail->setForceDelivery(false);
     $prefs->unsetPreference(PhabricatorUserPreferences::PREFERENCE_NO_MAIL);
     $prefs->save();
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.'));
     // Test that explicit exclusion works correctly.
     $mail->setExcludeMailRecipientPHIDs(array($phid));
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('Explicit exclude excludes recipients.'));
     $mail->setExcludeMailRecipientPHIDs(array());
     // Test that mail tag preferences exclude recipients.
     $prefs->setPreference(PhabricatorUserPreferences::PREFERENCE_MAILTAGS, array('test-tag' => false));
     $prefs->save();
     $mail->setMailTags(array('test-tag'));
     $this->assertFalse(in_array($phid, $mail->buildRecipientList()), pht('Tag preference excludes recipients.'));
     $prefs->unsetPreference(PhabricatorUserPreferences::PREFERENCE_MAILTAGS);
     $prefs->save();
     $this->assertTrue(in_array($phid, $mail->buildRecipientList()), 'Recipients restored after tag preference removed.');
 }
 public function send()
 {
     $to_phids = $this->getToPHIDs();
     if (!$to_phids) {
         throw new Exception('No "To:" users provided!');
     }
     $cc_phids = $this->getCCPHIDs();
     $subject = $this->buildSubject();
     $vary_subject = $this->buildVarySubject();
     $body = $this->buildBody();
     $attachments = $this->buildAttachments();
     $template = new PhabricatorMetaMTAMail();
     $actor_handle = $this->getActorHandle();
     $reply_handler = $this->getReplyHandler();
     if ($actor_handle) {
         $template->setFrom($actor_handle->getPHID());
     }
     $template->setSubject($subject)->setVarySubject($vary_subject)->setBody($body)->setIsHTML($this->shouldMarkMailAsHTML())->setParentMessageID($this->parentMessageID)->addHeader('Thread-Topic', $this->getRevision()->getTitle());
     $template->setAttachments($attachments);
     $template->setThreadID($this->getThreadID(), $this->isFirstMailAboutRevision());
     if ($this->heraldRulesHeader) {
         $template->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
     }
     $revision = $this->revision;
     if ($revision) {
         if ($revision->getAuthorPHID()) {
             $template->addHeader('X-Differential-Author', '<' . $revision->getAuthorPHID() . '>');
         }
         if ($revision->getReviewers()) {
             $template->addHeader('X-Differential-Reviewers', '<' . implode('>, <', $revision->getReviewers()) . '>');
         }
         if ($revision->getCCPHIDs()) {
             $template->addHeader('X-Differential-CCs', '<' . implode('>, <', $revision->getCCPHIDs()) . '>');
             // Determine explicit CCs (those added by humans) and put them in a
             // header so users can differentiate between Herald CCs and human CCs.
             $relation_subscribed = DifferentialRevision::RELATION_SUBSCRIBED;
             $raw = $revision->getRawRelations($relation_subscribed);
             $reason_phids = ipull($raw, 'reasonPHID');
             $reason_handles = id(new PhabricatorObjectHandleData($reason_phids))->loadHandles();
             $explicit_cc = array();
             foreach ($raw as $relation) {
                 if (!$relation['reasonPHID']) {
                     continue;
                 }
                 $type = $reason_handles[$relation['reasonPHID']]->getType();
                 if ($type == PhabricatorPHIDConstants::PHID_TYPE_USER) {
                     $explicit_cc[] = $relation['objectPHID'];
                 }
             }
             if ($explicit_cc) {
                 $template->addHeader('X-Differential-Explicit-CCs', '<' . implode('>, <', $explicit_cc) . '>');
             }
         }
     }
     $template->setIsBulk(true);
     $template->setRelatedPHID($this->getRevision()->getPHID());
     $mailtags = $this->getMailTags();
     if ($mailtags) {
         $template->setMailTags($mailtags);
     }
     $phids = array();
     foreach ($to_phids as $phid) {
         $phids[$phid] = true;
     }
     foreach ($cc_phids as $phid) {
         $phids[$phid] = true;
     }
     $phids = array_keys($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $event = new PhabricatorEvent(PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL, array('mail' => $template));
     PhutilEventEngine::dispatchEvent($event);
     $template = $event->getValue('mail');
     $mails = $reply_handler->multiplexMail($template, array_select_keys($handles, $to_phids), array_select_keys($handles, $cc_phids));
     foreach ($mails as $mail) {
         $mail->saveAndSend();
     }
 }
Example #5
0
 public function send()
 {
     $to_phids = $this->getToPHIDs();
     if (!$to_phids) {
         throw new Exception('No "To:" users provided!');
     }
     $cc_phids = $this->getCCPHIDs();
     $attachments = $this->buildAttachments();
     $template = new PhabricatorMetaMTAMail();
     $actor_handle = $this->getActorHandle();
     $reply_handler = $this->getReplyHandler();
     if ($actor_handle) {
         $template->setFrom($actor_handle->getPHID());
     }
     $template->setIsHTML($this->shouldMarkMailAsHTML())->setParentMessageID($this->parentMessageID)->addHeader('Thread-Topic', $this->getThreadTopic());
     $template->setAttachments($attachments);
     $template->setThreadID($this->getThreadID(), $this->isFirstMailAboutRevision());
     if ($this->heraldRulesHeader) {
         $template->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
     }
     $revision = $this->revision;
     if ($revision) {
         if ($revision->getAuthorPHID()) {
             $template->addHeader('X-Differential-Author', '<' . $revision->getAuthorPHID() . '>');
         }
         $reviewer_phids = $revision->getReviewers();
         if ($reviewer_phids) {
             // Add several headers to support e-mail clients which are not able to
             // create rules using regular expressions or wildcards (namely Outlook).
             $template->addPHIDHeaders('X-Differential-Reviewer', $reviewer_phids);
             // Add it also as a list to allow matching of the first reviewer and
             // also for backwards compatibility.
             $template->addHeader('X-Differential-Reviewers', '<' . implode('>, <', $reviewer_phids) . '>');
         }
         if ($cc_phids) {
             $template->addPHIDHeaders('X-Differential-CC', $cc_phids);
             $template->addHeader('X-Differential-CCs', '<' . implode('>, <', $cc_phids) . '>');
             // Determine explicit CCs (those added by humans) and put them in a
             // header so users can differentiate between Herald CCs and human CCs.
             $relation_subscribed = DifferentialRevision::RELATION_SUBSCRIBED;
             $raw = $revision->getRawRelations($relation_subscribed);
             $reason_phids = ipull($raw, 'reasonPHID');
             $reason_handles = id(new PhabricatorObjectHandleData($reason_phids))->loadHandles();
             $explicit_cc = array();
             foreach ($raw as $relation) {
                 if (!$relation['reasonPHID']) {
                     continue;
                 }
                 $type = $reason_handles[$relation['reasonPHID']]->getType();
                 if ($type == PhabricatorPHIDConstants::PHID_TYPE_USER) {
                     $explicit_cc[] = $relation['objectPHID'];
                 }
             }
             if ($explicit_cc) {
                 $template->addPHIDHeaders('X-Differential-Explicit-CC', $explicit_cc);
                 $template->addHeader('X-Differential-Explicit-CCs', '<' . implode('>, <', $explicit_cc) . '>');
             }
         }
     }
     $template->setIsBulk(true);
     $template->setRelatedPHID($this->getRevision()->getPHID());
     $mailtags = $this->getMailTags();
     if ($mailtags) {
         $template->setMailTags($mailtags);
     }
     $phids = array();
     foreach ($to_phids as $phid) {
         $phids[$phid] = true;
     }
     foreach ($cc_phids as $phid) {
         $phids[$phid] = true;
     }
     $phids = array_keys($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $objects = id(new PhabricatorObjectHandleData($phids))->loadObjects();
     $to_handles = array_select_keys($handles, $to_phids);
     $cc_handles = array_select_keys($handles, $cc_phids);
     $this->prepareBody();
     $mails = $reply_handler->multiplexMail($template, $to_handles, $cc_handles);
     $original_translator = PhutilTranslator::getInstance();
     if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $translation = PhabricatorEnv::newObjectFromConfig('translation.provider');
         $translator = id(new PhutilTranslator())->setLanguage($translation->getLanguage())->addTranslations($translation->getTranslations());
     }
     try {
         foreach ($mails as $mail) {
             if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
                 $translation = newv($mail->getTranslation($objects), array());
                 $translator = id(new PhutilTranslator())->setLanguage($translation->getLanguage())->addTranslations($translation->getTranslations());
                 PhutilTranslator::setInstance($translator);
             }
             $body = $this->buildBody() . "\n" . $reply_handler->getRecipientsSummary($to_handles, $cc_handles);
             $mail->setSubject($this->renderSubject())->setSubjectPrefix($this->getSubjectPrefix())->setVarySubjectPrefix($this->renderVaryPrefix())->setBody($body);
             $event = new PhabricatorEvent(PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL, array('mail' => $mail));
             PhutilEventEngine::dispatchEvent($event);
             $mail = $event->getValue('mail');
             $mail->saveAndSend();
         }
     } catch (Exception $ex) {
         PhutilTranslator::setInstance($original_translator);
         throw $ex;
     }
     PhutilTranslator::setInstance($original_translator);
 }