public function processRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $user = $this->getUser();
     $preferences = $user->loadPreferences();
     $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX;
     $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT;
     $prefs_html_email = PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS;
     $errors = array();
     if ($request->isFormPost()) {
         if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
             if ($request->getStr($pref_re_prefix) == 'default') {
                 $preferences->unsetPreference($pref_re_prefix);
             } else {
                 $preferences->setPreference($pref_re_prefix, $request->getBool($pref_re_prefix));
             }
             if ($request->getStr($pref_vary) == 'default') {
                 $preferences->unsetPreference($pref_vary);
             } else {
                 $preferences->setPreference($pref_vary, $request->getBool($pref_vary));
             }
             if ($request->getStr($prefs_html_email) == 'default') {
                 $preferences->unsetPreference($prefs_html_email);
             } else {
                 $preferences->setPreference($prefs_html_email, $request->getBool($prefs_html_email));
             }
         }
         $preferences->save();
         return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true'));
     }
     $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') ? pht('Enabled') : pht('Disabled');
     $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') ? pht('Vary') : pht('Do Not Vary');
     $html_emails_default = pht('Plain Text');
     $re_prefix_value = $preferences->getPreference($pref_re_prefix);
     if ($re_prefix_value === null) {
         $re_prefix_value = 'default';
     } else {
         $re_prefix_value = $re_prefix_value ? 'true' : 'false';
     }
     $vary_value = $preferences->getPreference($pref_vary);
     if ($vary_value === null) {
         $vary_value = 'default';
     } else {
         $vary_value = $vary_value ? 'true' : 'false';
     }
     $html_emails_value = $preferences->getPreference($prefs_html_email);
     if ($html_emails_value === null) {
         $html_emails_value = 'default';
     } else {
         $html_emails_value = $html_emails_value ? 'true' : 'false';
     }
     $form = new AphrontFormView();
     $form->setUser($viewer);
     if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $html_email_control = id(new AphrontFormSelectControl())->setName($prefs_html_email)->setOptions(array('default' => pht('Default (%s)', $html_emails_default), 'true' => pht('Send HTML Email'), 'false' => pht('Send Plain Text Email')))->setValue($html_emails_value);
         $re_control = id(new AphrontFormSelectControl())->setName($pref_re_prefix)->setOptions(array('default' => pht('Use Server Default (%s)', $re_prefix_default), 'true' => pht('Enable "Re:" prefix'), 'false' => pht('Disable "Re:" prefix')))->setValue($re_prefix_value);
         $vary_control = id(new AphrontFormSelectControl())->setName($pref_vary)->setOptions(array('default' => pht('Use Server Default (%s)', $vary_default), 'true' => pht('Vary Subjects'), 'false' => pht('Do Not Vary Subjects')))->setValue($vary_value);
     } else {
         $html_email_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $html_emails_default));
         $re_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $re_prefix_default));
         $vary_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $vary_default));
     }
     $form->appendRemarkupInstructions(pht('These settings fine-tune some technical aspects of how email is ' . 'formatted. You may be able to adjust them to make mail more ' . 'useful or improve threading.'));
     if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $form->appendRemarkupInstructions(pht('NOTE: This install of Phabricator is configured to send a ' . 'single mail message to all recipients, so all settings are ' . 'locked at the server default value.'));
     }
     $form->appendRemarkupInstructions(pht("You can use the **HTML Email** setting to control whether " . "Phabricator send you HTML email (which has more color and " . "formatting) or plain text email (which is more compatible).\n" . "\n" . "WARNING: This feature is new and experimental! If you enable " . "it, mail may not render properly and replying to mail may not " . "work as well."))->appendChild($html_email_control->setLabel(pht('HTML Email')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('The **Add "Re:" Prefix** setting adds "Re:" in front of all ' . 'messages, even if they are not replies. If you use **Mail.app** on ' . 'Mac OS X, this may improve mail threading.' . "\n\n" . "| Setting                | Example Mail Subject\n" . "|------------------------|----------------\n" . "| Enable \"Re:\" Prefix  | " . "`Re: [Differential] [Accepted] D123: Example Revision`\n" . "| Disable \"Re:\" Prefix | " . "`[Differential] [Accepted] D123: Example Revision`"))->appendChild($re_control->setLabel(pht('Add "Re:" Prefix')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('With **Vary Subjects** enabled, most mail subject lines will ' . 'include a brief description of their content, like **[Closed]** ' . 'for a notification about someone closing a task.' . "\n\n" . "| Setting              | Example Mail Subject\n" . "|----------------------|----------------\n" . "| Vary Subjects        | " . "`[Maniphest] [Closed] T123: Example Task`\n" . "| Do Not Vary Subjects | " . "`[Maniphest] T123: Example Task`\n" . "\n" . 'This can make mail more useful, but some clients have difficulty ' . 'threading these messages. Disabling this option may improve ' . 'threading, at the cost of less useful subject lines.'))->appendChild($vary_control->setLabel(pht('Vary Subjects')));
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Preferences')));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Email Format'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form);
     return id(new AphrontNullView())->appendChild(array($form_box));
 }
 public final function multiplexMail(PhabricatorMetaMTAMail $mail_template, array $to_handles, array $cc_handles)
 {
     assert_instances_of($to_handles, 'PhabricatorObjectHandle');
     assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
     $result = array();
     // If MetaMTA is configured to always multiplex, skip the single-email
     // case.
     if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         // If private replies are not supported, simply send one email to all
         // recipients and CCs. This covers cases where we have no reply handler,
         // or we have a public reply handler.
         if (!$this->supportsPrivateReplies()) {
             $mail = clone $mail_template;
             $mail->addTos(mpull($to_handles, 'getPHID'));
             $mail->addCCs(mpull($cc_handles, 'getPHID'));
             if ($this->supportsPublicReplies()) {
                 $reply_to = $this->getPublicReplyHandlerEmailAddress();
                 $mail->setReplyTo($reply_to);
             }
             $result[] = $mail;
             return $result;
         }
     }
     $tos = mpull($to_handles, null, 'getPHID');
     $ccs = mpull($cc_handles, null, 'getPHID');
     // Merge all the recipients together. TODO: We could keep the CCs as real
     // CCs and send to a "*****@*****.**" type address, but keep it simple
     // for now.
     $recipients = $tos + $ccs;
     // When multiplexing mail, explicitly include To/Cc information in the
     // message body and headers.
     $mail_template = clone $mail_template;
     $mail_template->addPHIDHeaders('X-Phabricator-To', array_keys($tos));
     $mail_template->addPHIDHeaders('X-Phabricator-Cc', array_keys($ccs));
     $body = $mail_template->getBody();
     $body .= "\n";
     $body .= $this->getRecipientsSummary($to_handles, $cc_handles);
     foreach ($recipients as $phid => $recipient) {
         $mail = clone $mail_template;
         if (isset($to_handles[$phid])) {
             $mail->addTos(array($phid));
         } else {
             if (isset($cc_handles[$phid])) {
                 $mail->addCCs(array($phid));
             } else {
                 // not good - they should be a to or a cc
                 continue;
             }
         }
         $mail->setBody($body);
         $reply_to = null;
         if (!$reply_to && $this->supportsPrivateReplies()) {
             $reply_to = $this->getPrivateReplyHandlerEmailAddress($recipient);
         }
         if (!$reply_to && $this->supportsPublicReplies()) {
             $reply_to = $this->getPublicReplyHandlerEmailAddress();
         }
         if ($reply_to) {
             $mail->setReplyTo($reply_to);
         }
         $result[] = $mail;
     }
     return $result;
 }
 public function isUserPanel()
 {
     return PhabricatorMetaMTAMail::shouldMultiplexAllMail();
 }
 public function processRequest(AphrontRequest $request)
 {
     $user = $request->getUser();
     $preferences = $user->loadPreferences();
     $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX;
     $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT;
     $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
     $errors = array();
     if ($request->isFormPost()) {
         if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
             if ($request->getStr($pref_re_prefix) == 'default') {
                 $preferences->unsetPreference($pref_re_prefix);
             } else {
                 $preferences->setPreference($pref_re_prefix, $request->getBool($pref_re_prefix));
             }
             if ($request->getStr($pref_vary) == 'default') {
                 $preferences->unsetPreference($pref_vary);
             } else {
                 $preferences->setPreference($pref_vary, $request->getBool($pref_vary));
             }
         }
         $preferences->setPreference($pref_no_self_mail, $request->getStr($pref_no_self_mail));
         $new_tags = $request->getArr('mailtags');
         $mailtags = $preferences->getPreference('mailtags', array());
         foreach ($this->getMailTags() as $key => $label) {
             $mailtags[$key] = (bool) idx($new_tags, $key, false);
         }
         $preferences->setPreference('mailtags', $mailtags);
         $preferences->save();
         return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true'));
     }
     $notice = null;
     if (!$errors) {
         if ($request->getStr('saved')) {
             $notice = new AphrontErrorView();
             $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
             $notice->setTitle('Changes Saved');
             $notice->appendChild('<p>Your changes have been saved.</p>');
         }
     } else {
         $notice = new AphrontErrorView();
         $notice->setTitle('Form Errors');
         $notice->setErrors($errors);
     }
     $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') ? 'Enabled' : 'Disabled';
     $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') ? 'Vary' : 'Do Not Vary';
     $re_prefix_value = $preferences->getPreference($pref_re_prefix);
     if ($re_prefix_value === null) {
         $re_prefix_value = 'default';
     } else {
         $re_prefix_value = $re_prefix_value ? 'true' : 'false';
     }
     $vary_value = $preferences->getPreference($pref_vary);
     if ($vary_value === null) {
         $vary_value = 'default';
     } else {
         $vary_value = $vary_value ? 'true' : 'false';
     }
     $form = new AphrontFormView();
     $form->setUser($user)->appendChild(id(new AphrontFormSelectControl())->setLabel('Self Actions')->setName($pref_no_self_mail)->setOptions(array('0' => 'Send me an email when I take an action', '1' => 'Do not send me an email when I take an action'))->setCaption('You can disable email about your own actions.')->setValue($preferences->getPreference($pref_no_self_mail, 0)));
     if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $re_control = id(new AphrontFormSelectControl())->setName($pref_re_prefix)->setOptions(array('default' => 'Use Server Default (' . $re_prefix_default . ')', 'true' => 'Enable "Re:" prefix', 'false' => 'Disable "Re:" prefix'))->setValue($re_prefix_value);
         $vary_control = id(new AphrontFormSelectControl())->setName($pref_vary)->setOptions(array('default' => 'Use Server Default (' . $vary_default . ')', 'true' => 'Vary Subjects', 'false' => 'Do Not Vary Subjects'))->setValue($vary_value);
     } else {
         $re_control = id(new AphrontFormStaticControl())->setValue('Server Default (' . $re_prefix_default . ')');
         $vary_control = id(new AphrontFormStaticControl())->setValue('Server Default (' . $vary_default . ')');
     }
     $form->appendChild($re_control->setLabel('Add "Re:" Prefix')->setCaption('Enable this option to fix threading in Mail.app on OS X Lion, ' . 'or if you like "Re:" in your email subjects.'))->appendChild($vary_control->setLabel('Vary Subjects')->setCaption('This option adds more information to email subjects, but may ' . 'break threading in some clients.'));
     $form->appendChild('<br />' . '<p class="aphront-form-instructions">' . 'You can customize what mail you receive from Phabricator here.' . '</p>' . '<p class="aphront-form-instructions">' . '<strong>NOTE:</strong> If an update makes several changes (like ' . 'adding CCs to a task, closing it, and adding a comment) you will ' . 'still receive an email as long as at least one of the changes ' . 'is set to notify you.' . '</p>');
     $mailtags = $preferences->getPreference('mailtags', array());
     $form->appendChild($this->buildMailTagCheckboxes($this->getDifferentialMailTags(), $mailtags)->setLabel('Differential'))->appendChild($this->buildMailTagCheckboxes($this->getManiphestMailTags(), $mailtags)->setLabel('Maniphest'));
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Save Preferences'));
     $panel = new AphrontPanelView();
     $panel->setHeader('Email Preferences');
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     $panel->appendChild($form);
     return id(new AphrontNullView())->appendChild(array($notice, $panel));
 }
 public final function multiplexMail(PhabricatorMetaMTAMail $mail_template, array $to_handles, array $cc_handles)
 {
     assert_instances_of($to_handles, 'PhabricatorObjectHandle');
     assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
     $result = array();
     // If MetaMTA is configured to always multiplex, skip the single-email
     // case.
     if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         // If private replies are not supported, simply send one email to all
         // recipients and CCs. This covers cases where we have no reply handler,
         // or we have a public reply handler.
         if (!$this->supportsPrivateReplies()) {
             $mail = clone $mail_template;
             $mail->addTos(mpull($to_handles, 'getPHID'));
             $mail->addCCs(mpull($cc_handles, 'getPHID'));
             if ($this->supportsPublicReplies()) {
                 $reply_to = $this->getPublicReplyHandlerEmailAddress();
                 $mail->setReplyTo($reply_to);
             }
             $result[] = $mail;
             return $result;
         }
     }
     // Merge all the recipients together. TODO: We could keep the CCs as real
     // CCs and send to a "*****@*****.**" type address, but keep it simple
     // for now.
     $recipients = mpull($to_handles, null, 'getPHID') + mpull($cc_handles, null, 'getPHID');
     // When multiplexing mail, explicitly include To/Cc information in the
     // message body and headers.
     $add_headers = array();
     $body = $mail_template->getBody();
     $body .= "\n";
     if ($to_handles) {
         $body .= "To: " . implode(', ', mpull($to_handles, 'getName')) . "\n";
         $add_headers['X-Phabricator-To'] = $this->formatPHIDList($to_handles);
     }
     if ($cc_handles) {
         $body .= "Cc: " . implode(', ', mpull($cc_handles, 'getName')) . "\n";
         $add_headers['X-Phabricator-Cc'] = $this->formatPHIDList($cc_handles);
     }
     foreach ($recipients as $recipient) {
         $mail = clone $mail_template;
         $mail->addTos(array($recipient->getPHID()));
         $mail->setBody($body);
         foreach ($add_headers as $header => $value) {
             $mail->addHeader($header, $value);
         }
         $reply_to = null;
         if (!$reply_to && $this->supportsPrivateReplies()) {
             $reply_to = $this->getPrivateReplyHandlerEmailAddress($recipient);
         }
         if (!$reply_to && $this->supportsPublicReplies()) {
             $reply_to = $this->getPublicReplyHandlerEmailAddress();
         }
         if ($reply_to) {
             $mail->setReplyTo($reply_to);
         }
         $result[] = $mail;
     }
     return $result;
 }
Esempio n. 6
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);
 }