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 @@ -322,6 +322,7 @@ 'DifferentialCreateCommentConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php', 'DifferentialCreateDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php', 'DifferentialCreateInlineConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php', + 'DifferentialCreateMailReceiver' => 'applications/differential/mail/DifferentialCreateMailReceiver.php', 'DifferentialCreateRawDiffConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php', 'DifferentialCreateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php', 'DifferentialCustomField' => 'applications/differential/customfield/DifferentialCustomField.php', @@ -3421,6 +3422,7 @@ 'DifferentialCreateCommentConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialCreateDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialCreateInlineConduitAPIMethod' => 'DifferentialConduitAPIMethod', + 'DifferentialCreateMailReceiver' => 'PhabricatorMailReceiver', 'DifferentialCreateRawDiffConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialCreateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialCustomField' => 'PhabricatorCustomField', diff --git a/src/applications/differential/application/PhabricatorDifferentialApplication.php b/src/applications/differential/application/PhabricatorDifferentialApplication.php --- a/src/applications/differential/application/PhabricatorDifferentialApplication.php +++ b/src/applications/differential/application/PhabricatorDifferentialApplication.php @@ -162,6 +162,22 @@ return $status; } + public function supportsEmailIntegration() { + return true; + } + + public function getAppEmailBlurb() { + return pht( + 'Send email to these addresses to create revisions. The body of the '. + 'message and / or one or more attachments should be the output of a '. + '"diff" command. %s', + phutil_tag( + 'a', + array( + 'href' => $this->getInboundEmailSupportLink(),), + pht('Learn More'))); + } + protected function getCustomCapabilities() { return array( DifferentialDefaultViewCapability::CAPABILITY => array( diff --git a/src/applications/differential/conduit/DifferentialConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialConduitAPIMethod.php --- a/src/applications/differential/conduit/DifferentialConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialConduitAPIMethod.php @@ -12,8 +12,9 @@ $uri = PhabricatorEnv::getProductionURI($uri); return array( - 'id' => $diff->getID(), - 'uri' => $uri, + 'id' => $diff->getID(), + 'phid' => $diff->getPHID(), + 'uri' => $uri, ); } diff --git a/src/applications/differential/mail/DifferentialCreateMailReceiver.php b/src/applications/differential/mail/DifferentialCreateMailReceiver.php new file mode 100644 --- /dev/null +++ b/src/applications/differential/mail/DifferentialCreateMailReceiver.php @@ -0,0 +1,123 @@ +canAcceptApplicationMail($differential_app, $mail); + } + + protected function processReceivedMail( + PhabricatorMetaMTAReceivedMail $mail, + PhabricatorUser $sender) { + + $attachments = $mail->getAttachments(); + $files = array(); + $errors = array(); + if ($attachments) { + $files = id(new PhabricatorFileQuery()) + ->setViewer($sender) + ->withPHIDs($attachments) + ->execute(); + foreach ($files as $index => $file) { + if ($file->getMimeType() != 'text/plain') { + $errors[] = pht( + 'Could not parse file %s; only files with mimetype text/plain '. + 'can be parsed via email.', + $file->getName()); + unset($files[$index]); + } + } + } + + $diffs = array(); + foreach ($files as $file) { + $call = new ConduitCall( + 'differential.createrawdiff', + array( + 'diff' => $file->loadFileData(), + )); + $call->setUser($sender); + try { + $result = $call->execute(); + $diffs[$file->getName()] = $result['uri']; + } catch (Exception $e) { + $errors[] = pht( + 'Could not parse attachment %s; only attachments (and mail bodies) '. + 'generated via "diff" commands can be parsed.', + $file->getName()); + } + } + + $body = $mail->getCleanTextBody(); + if ($body) { + $call = new ConduitCall( + 'differential.createrawdiff', + array( + 'diff' => $body,)); + $call->setUser($sender); + try { + $result = $call->execute(); + $diffs[pht('Mail Body')] = $result['uri']; + } catch (Exception $e) { + $errors[] = pht( + 'Could not parse mail body; only mail bodies (and attachments) '. + 'generated via "diff" commands can be parsed.'); + } + } + + $subject_prefix = + PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix'); + if (count($diffs)) { + $subject = pht( + 'You successfully created %d diff(s).', + count($diffs)); + } else { + $subject = pht( + 'Diff creation failed; see body for error(s).', + count($errors)); + } + $body = new PhabricatorMetaMTAMailBody(); + $body->addRawSection($subject); + if (count($diffs)) { + $text_body = ''; + $html_body = array(); + $body_label = pht('DIFF LINK(S)', count($diffs)); + foreach ($diffs as $filename => $diff_uri) { + $text_body .= $filename.': '.$diff_uri."\n"; + $html_body[] = phutil_tag( + 'a', + array( + 'href' => $diff_uri, + ), + $filename); + $html_body[] = phutil_tag('br'); + } + $body->addTextSection($body_label, $text_body); + $body->addHTMLSection($body_label, $html_body); + } + + if (count($errors)) { + $body_section = new PhabricatorMetaMTAMailSection(); + $body_label = pht('ERROR(S)', count($errors)); + foreach ($errors as $error) { + $body_section->addFragment($error); + } + $body->addTextSection($body_label, $body_section); + } + + id(new PhabricatorMetaMTAMail()) + ->addTos(array($sender->getPHID())) + ->setSubject($subject) + ->setSubjectPrefix($subject_prefix) + ->setFrom($sender->getPHID()) + ->setBody($body->render()) + ->saveAndSend(); + } + +} diff --git a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php --- a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php @@ -19,6 +19,7 @@ ), 'Task(s)' => array('Task', 'Tasks'), + 'ERROR(S)' => array('ERROR', 'ERRORS'), '%d Error(s)' => array('%d Error', '%d Errors'), '%d Warning(s)' => array('%d Warning', '%d Warnings'), '%d Auto-Fix(es)' => array('%d Auto-Fix', '%d Auto-Fixes'), @@ -31,6 +32,16 @@ '%d path(s)' => array('%d path', '%d paths'), '%d diff(s)' => array('%d diff', '%d diffs'), + 'DIFF LINK(S)' => array('DIFF LINK', 'DIFF LINKS'), + 'You successfully created %d diff(s).' => array( + 'You successfully created %d diff.', + 'You successfully created %d diffs.', + ), + 'Diff creation failed; see body for error(s).' => array( + 'Diff creation failed; see body for error.', + 'Diff creation failed; see body for errors.', + ), + 'There are %d raw fact(s) in storage.' => array( 'There is %d raw fact in storage.', 'There are %d raw facts in storage.',