diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1728,6 +1728,7 @@ 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/PhabricatorMetaMTAMail.php', 'PhabricatorMetaMTAMailBody' => 'applications/metamta/view/PhabricatorMetaMTAMailBody.php', 'PhabricatorMetaMTAMailBodyTestCase' => 'applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php', + 'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php', 'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php', 'PhabricatorMetaMTAMailableDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php', 'PhabricatorMetaMTAMailgunReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php', diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -1179,23 +1179,21 @@ $config_attach = PhabricatorEnv::getEnvConfig($config_key_attach); if ($config_inline || $config_attach) { - list($patch, $html_patch) = $this->renderPatchForMail($diff); - $lines = count(phutil_split_lines($patch)); + $patchSection = $this->renderPatchForMail($diff); + $lines = count(phutil_split_lines($patchSection->getPlaintext())); if ($config_inline && ($lines <= $config_inline)) { - $body->addPlaintextSection( + $body->addTextSection( pht('CHANGE DETAILS'), - $patch); - $body->addHTMLSection( - pht('CHANGE DETAILS'), - $html_patch); + $patchSection); } if ($config_attach) { $name = pht('D%s.%s.patch', $object->getID(), $diff->getID()); $mime_type = 'text/x-patch; charset=utf-8'; $body->addAttachment( - new PhabricatorMetaMTAAttachment($patch, $name, $mime_type)); + new PhabricatorMetaMTAAttachment( + $patchSection->getPlaintext(), $name, $mime_type)); } } } @@ -1333,7 +1331,7 @@ $hunk_parser = new DifferentialHunkParser(); } - $result = array(); + $section = new PhabricatorMetaMTAMailSection(); foreach ($inline_groups as $changeset_id => $group) { $changeset = idx($changesets, $changeset_id); if (!$changeset) { @@ -1354,25 +1352,27 @@ $inline_content = $comment->getContent(); if (!$show_context) { - $result[] = "{$file}:{$range} {$inline_content}"; + $section->addFragment("{$file}:{$range} {$inline_content}"); } else { - $result[] = '================'; - $result[] = 'Comment at: '.$file.':'.$range; - $result[] = $hunk_parser->makeContextDiff( + $patch = $hunk_parser->makeContextDiff( $changeset->getHunks(), $comment->getIsNewFile(), $comment->getLineNumber(), $comment->getLineLength(), 1); - $result[] = '----------------'; - $result[] = $inline_content; - $result[] = null; + $section->addFragment('================') + ->addFragment('Comment at: '.$file.':'.$range) + ->addPlaintextFragment($patch) + ->addHTMLFragment($this->renderPatchHTMLForMail($patch)) + ->addFragment('----------------') + ->addFragment($inline_content) + ->addFragment(null); } } } - return implode("\n", $result); + return $section; } private function loadDiff($phid, $need_changesets = false) { @@ -1765,6 +1765,11 @@ return implode("\n", $filenames); } + private function renderPatchHTMLForMail($patch) { + return phutil_tag('pre', + array('style' => 'font-family: monospace;'), $patch); + } + private function renderPatchForMail(DifferentialDiff $diff) { $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); @@ -1774,25 +1779,11 @@ ->setChangesets($diff->getChangesets()) ->buildPatch(); - // some basic formatting (switch to a PhutilSyntaxHighlighterEngine?) - $patch_html_lines = array(); - foreach (phutil_split_lines($patch) as $line) { - $len = strlen($line); - if ($len >= 1 && $line[0] == '+') { - $patch_html_lines[] = "${line}"; - } else if ($len >= 1 && $line[0] == '-') { - $patch_html_lines[] = "${line}"; - } else if ($len >= 2 && substr($line, 0, 2) == '@@') { - $patch_html_lines[] = "${line}"; - } else { - $patch_html_lines[] = "${line}"; - } - } - - $html_patch = "
". - implode('
', $patch_html_lines).'
'; + $section = new PhabricatorMetaMTAMailSection(); + $section->addHTMLFragment($this->renderPatchHTMLForMail($patch)); + $section->addPlaintextFragment($patch); - return array($patch, $html_patch); + return $section; } } diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -116,7 +116,6 @@ ->addCCs($ccs) ->setSubject($subject) ->setBody($body) - ->setIsHTML($is_html) ->setIsBulk($is_bulk) ->setMailTags($tags); diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -214,6 +214,7 @@ public function setHTMLBody($html) { $this->setParam('html-body', $html); + return $this; } public function getBody() { @@ -376,6 +377,32 @@ $add_cc = array(); $add_to = array(); + // Only try to use preferences if everything is multiplexed, so we + // get consistent behavior. + $use_prefs = self::shouldMultiplexAllMail(); + + $prefs = null; + if ($use_prefs) { + + // If multiplexing is enabled, some recipients will be in "Cc" + // rather than "To". We'll move them to "To" later (or supply a + // dummy "To") but need to look for the recipient in either the + // "To" or "Cc" fields here. + $target_phid = head(idx($params, 'to', array())); + if (!$target_phid) { + $target_phid = head(idx($params, 'cc', array())); + } + + if ($target_phid) { + $user = id(new PhabricatorUser())->loadOneWhere( + 'phid = %s', + $target_phid); + if ($user) { + $prefs = $user->loadPreferences(); + } + } + } + foreach ($params as $key => $value) { switch ($key) { case 'from': @@ -444,32 +471,6 @@ } break; case 'subject': - // Only try to use preferences if everything is multiplexed, so we - // get consistent behavior. - $use_prefs = self::shouldMultiplexAllMail(); - - $prefs = null; - if ($use_prefs) { - - // If multiplexing is enabled, some recipients will be in "Cc" - // rather than "To". We'll move them to "To" later (or supply a - // dummy "To") but need to look for the recipient in either the - // "To" or "Cc" fields here. - $target_phid = head(idx($params, 'to', array())); - if (!$target_phid) { - $target_phid = head(idx($params, 'cc', array())); - } - - if ($target_phid) { - $user = id(new PhabricatorUser())->loadOneWhere( - 'phid = %s', - $target_phid); - if ($user) { - $prefs = $user->loadPreferences(); - } - } - } - $subject = array(); if ($is_threaded) { @@ -555,7 +556,7 @@ } } - $body = $params['body']; + $body = idx($params, 'body', ''); $max = PhabricatorEnv::getEnvConfig('metamta.email-body-limit'); if (strlen($body) > $max) { $body = phutil_utf8_shorten($body, $max); @@ -564,8 +565,14 @@ } $mailer->setBody($body); - if (PhabricatorEnv::getEnvConfig('metamta.html-emails') && - isset($params['html-body'])) { + $html_emails = PhabricatorEnv::getEnvConfig('metamta.html-emails'); + if ($use_prefs && $prefs) { + $html_emails = $prefs->getPreference( + PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS, + $html_emails); + } + + if ($html_emails && isset($params['html-body'])) { $mailer->setHTMLBody($params['html-body']); } diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php --- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php @@ -48,6 +48,7 @@ return $this; } + /** * Add a block of text with a section header. This is rendered like this: * @@ -59,10 +60,17 @@ * @return this * @task compose */ - public function addTextSection($header, $text) { - $this->addPlaintextSection($header, $text); - $this->addHTMLSection($header, - phutil_escape_html_newlines(phutil_tag('div', array(), $text))); + public function addTextSection($header, $section) { + if ($section instanceof PhabricatorMetaMTAMailSection) { + $plaintext = $section->getPlaintext(); + $html = $section->getHTML(); + } else { + $plaintext = $section; + $html = phutil_escape_html_newlines(phutil_tag('div', array(), $section)); + } + + $this->addPlaintextSection($header, $plaintext); + $this->addHTMLSection($header, $html); return $this; } diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailSection.php b/src/applications/metamta/view/PhabricatorMetaMTAMailSection.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailSection.php @@ -0,0 +1,40 @@ +htmlFragments); + } + + public function getPlaintext() { + return implode('\n', $this->plaintextFragments); + } + + public function addHTMLFragment($fragment) { + $this->htmlFragments[] = $fragment; + return $this; + + } + + public function addPlaintextFragment($fragment) { + $this->plaintextFragments[] = $fragment; + return $this; + } + + public function addFragment($fragment) { + $this->plaintextFragments[] = $fragment; + $this->htmlFragments[] = + phutil_escape_html_newlines(phutil_tag('div', array(), $fragment)); + + return $this; + } +} diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelEmailFormat.php b/src/applications/settings/panel/PhabricatorSettingsPanelEmailFormat.php --- a/src/applications/settings/panel/PhabricatorSettingsPanelEmailFormat.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanelEmailFormat.php @@ -22,6 +22,7 @@ $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()) { @@ -42,6 +43,14 @@ $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(); @@ -58,6 +67,10 @@ ? pht('Vary') : pht('Do Not Vary'); + $html_emails_default = PhabricatorEnv::getEnvConfig('metamta.html-emails') + ? pht('HTML') + : pht('plaintext'); + $re_prefix_value = $preferences->getPreference($pref_re_prefix); if ($re_prefix_value === null) { $re_prefix_value = 'default'; @@ -76,11 +89,30 @@ : '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($user); if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { + $html_email_control = id(new AphrontFormSelectControl()) + ->setName($prefs_html_email) + ->setOptions( + array( + 'default' => pht('Use Server Default (%s)', $html_emails_default), + 'true' => pht('HTML emails'), + 'false' => pht('Plaintext emails'), + )) + ->setValue($html_emails_value); + $re_control = id(new AphrontFormSelectControl()) ->setName($pref_re_prefix) ->setOptions( @@ -101,6 +133,9 @@ )) ->setValue($vary_value); } else { + $html_email_control = id(new AphrontFormStaticControl()) + ->setValue('Server Default ('.$html_emails_default.')'); + $re_control = id(new AphrontFormStaticControl()) ->setValue('Server Default ('.$re_prefix_default.')'); @@ -124,6 +159,7 @@ } $form + ->appendChild($html_email_control) ->appendRemarkupInstructions('') ->appendRemarkupInstructions( pht( diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -15,6 +15,7 @@ const PREFERENCE_NO_MAIL = 'no-mail'; const PREFERENCE_MAILTAGS = 'mailtags'; const PREFERENCE_VARY_SUBJECT = 'vary-subject'; + const PREFERENCE_HTML_EMAILS = 'html-emails'; const PREFERENCE_SEARCHBAR_JUMP = 'searchbar-jump'; const PREFERENCE_SEARCH_SHORTCUT = 'search-shortcut';