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(); } }
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); }