diff --git a/scripts/user/add_user.php b/scripts/user/add_user.php --- a/scripts/user/add_user.php +++ b/scripts/user/add_user.php @@ -59,7 +59,12 @@ ->setActor($admin) ->createNewUser($user, $email_object); -$user->sendWelcomeEmail($admin); +$welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine()) + ->setSender($admin) + ->setRecipient($user); +if ($welcome_engine->canSendMail()) { + $welcome_engine->sendMail(); +} echo pht( "Created user '%s' (realname='%s', email='%s').\n", 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 @@ -3814,6 +3814,8 @@ 'PhabricatorPeopleLogQuery' => 'applications/people/query/PhabricatorPeopleLogQuery.php', 'PhabricatorPeopleLogSearchEngine' => 'applications/people/query/PhabricatorPeopleLogSearchEngine.php', 'PhabricatorPeopleLogsController' => 'applications/people/controller/PhabricatorPeopleLogsController.php', + 'PhabricatorPeopleMailEngine' => 'applications/people/mail/PhabricatorPeopleMailEngine.php', + 'PhabricatorPeopleMailEngineException' => 'applications/people/mail/PhabricatorPeopleMailEngineException.php', 'PhabricatorPeopleManageProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php', 'PhabricatorPeopleManagementWorkflow' => 'applications/people/management/PhabricatorPeopleManagementWorkflow.php', 'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php', @@ -3841,6 +3843,7 @@ 'PhabricatorPeopleUserFunctionDatasource' => 'applications/people/typeahead/PhabricatorPeopleUserFunctionDatasource.php', 'PhabricatorPeopleUserPHIDType' => 'applications/people/phid/PhabricatorPeopleUserPHIDType.php', 'PhabricatorPeopleWelcomeController' => 'applications/people/controller/PhabricatorPeopleWelcomeController.php', + 'PhabricatorPeopleWelcomeMailEngine' => 'applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php', 'PhabricatorPhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorPhabricatorAuthProvider.php', 'PhabricatorPhameApplication' => 'applications/phame/application/PhabricatorPhameApplication.php', 'PhabricatorPhameBlogPHIDType' => 'applications/phame/phid/PhabricatorPhameBlogPHIDType.php', @@ -9730,6 +9733,8 @@ 'PhabricatorPeopleLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorPeopleLogSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleMailEngine' => 'Phobject', + 'PhabricatorPeopleMailEngineException' => 'Exception', 'PhabricatorPeopleManageProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorPeopleManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorPeopleNewController' => 'PhabricatorPeopleController', @@ -9757,6 +9762,7 @@ 'PhabricatorPeopleUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorPeopleUserPHIDType' => 'PhabricatorPHIDType', 'PhabricatorPeopleWelcomeController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleWelcomeMailEngine' => 'PhabricatorPeopleMailEngine', 'PhabricatorPhabricatorAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorPhameApplication' => 'PhabricatorApplication', 'PhabricatorPhameBlogPHIDType' => 'PhabricatorPHIDType', diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php --- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php @@ -115,7 +115,14 @@ $info[] = $this->newSectionHeader(pht('HEADERS')); $headers = $message->getDeliveredHeaders(); + if (!$headers) { + $headers = array(); + } + $unfiltered = $message->getUnfilteredHeaders(); + if (!$unfiltered) { + $unfiltered = array(); + } $header_map = array(); foreach ($headers as $header) { @@ -201,6 +208,7 @@ $info[] = null; } else { $info[] = pht('(This message has no HTML body.)'); + $info[] = null; } $console->writeOut('%s', implode("\n", $info)); diff --git a/src/applications/people/controller/PhabricatorPeopleNewController.php b/src/applications/people/controller/PhabricatorPeopleNewController.php --- a/src/applications/people/controller/PhabricatorPeopleNewController.php +++ b/src/applications/people/controller/PhabricatorPeopleNewController.php @@ -107,8 +107,13 @@ ->makeMailingListUser($user, true); } - if ($welcome_checked && !$is_bot && !$is_list) { - $user->sendWelcomeEmail($admin); + if ($welcome_checked) { + $welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine()) + ->setSender($admin) + ->setRecipient($user); + if ($welcome_engine->canSendMail()) { + $welcome_engine->sendMail(); + } } $response = id(new AphrontRedirectResponse()) diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php --- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php @@ -92,8 +92,11 @@ PeopleDisableUsersCapability::CAPABILITY); $can_disable = ($has_disable && !$is_self); - $can_welcome = ($is_admin && $user->canEstablishWebSessions()); + $welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine()) + ->setSender($viewer) + ->setRecipient($user); + $can_welcome = $welcome_engine->canSendMail(); $curtain = $this->newCurtainView($user); $curtain->addAction( diff --git a/src/applications/people/controller/PhabricatorPeopleWelcomeController.php b/src/applications/people/controller/PhabricatorPeopleWelcomeController.php --- a/src/applications/people/controller/PhabricatorPeopleWelcomeController.php +++ b/src/applications/people/controller/PhabricatorPeopleWelcomeController.php @@ -3,6 +3,13 @@ final class PhabricatorPeopleWelcomeController extends PhabricatorPeopleController { + public function shouldRequireAdmin() { + // You need to be an administrator to actually send welcome email, but + // we let anyone hit this page so they can get a nice error dialog + // explaining the issue. + return false; + } + public function handleRequest(AphrontRequest $request) { $admin = $this->getViewer(); @@ -14,22 +21,24 @@ return new Aphront404Response(); } - $profile_uri = '/p/'.$user->getUsername().'/'; + $id = $user->getID(); + $profile_uri = "/people/manage/{$id}/"; + + $welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine()) + ->setSender($admin) + ->setRecipient($user); - if (!$user->canEstablishWebSessions()) { + try { + $welcome_engine->validateMail(); + } catch (PhabricatorPeopleMailEngineException $ex) { return $this->newDialog() - ->setTitle(pht('Not a Normal User')) - ->appendParagraph( - pht( - 'You can not send this user a welcome mail because they are not '. - 'a normal user and can not log in to the web interface. Special '. - 'users (like bots and mailing lists) are unable to establish web '. - 'sessions.')) + ->setTitle($ex->getTitle()) + ->appendParagraph($ex->getBody()) ->addCancelButton($profile_uri, pht('Done')); } if ($request->isFormPost()) { - $user->sendWelcomeEmail($admin); + $welcome_engine->sendMail(); return id(new AphrontRedirectResponse())->setURI($profile_uri); } diff --git a/src/applications/people/mail/PhabricatorPeopleMailEngine.php b/src/applications/people/mail/PhabricatorPeopleMailEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/people/mail/PhabricatorPeopleMailEngine.php @@ -0,0 +1,61 @@ +sender = $sender; + return $this; + } + + final public function getSender() { + if (!$this->sender) { + throw new PhutilInvalidStateException('setSender'); + } + return $this->sender; + } + + final public function setRecipient(PhabricatorUser $recipient) { + $this->recipient = $recipient; + return $this; + } + + final public function getRecipient() { + if (!$this->recipient) { + throw new PhutilInvalidStateException('setRecipient'); + } + return $this->recipient; + } + + final public function canSendMail() { + try { + $this->validateMail(); + return true; + } catch (PhabricatorPeopleMailEngineException $ex) { + return false; + } + } + + final public function sendMail() { + $this->validateMail(); + $mail = $this->newMail(); + + $mail + ->setForceDelivery(true) + ->save(); + + return $mail; + } + + abstract public function validateMail(); + abstract protected function newMail(); + + + final protected function throwValidationException($title, $body) { + throw new PhabricatorPeopleMailEngineException($title, $body); + } + +} diff --git a/src/applications/people/mail/PhabricatorPeopleMailEngineException.php b/src/applications/people/mail/PhabricatorPeopleMailEngineException.php new file mode 100644 --- /dev/null +++ b/src/applications/people/mail/PhabricatorPeopleMailEngineException.php @@ -0,0 +1,24 @@ +title = $title; + $this->body = $body; + + parent::__construct(pht('%s: %s', $title, $body)); + } + + public function getTitle() { + return $this->title; + } + + public function getBody() { + return $this->body; + } + +} diff --git a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php @@ -0,0 +1,83 @@ +getSender(); + $recipient = $this->getRecipient(); + + if (!$sender->getIsAdmin()) { + $this->throwValidationException( + pht('Not an Administrator'), + pht( + 'You can not send welcome mail because you are not an '. + 'administrator. Only administrators may send welcome mail.')); + } + + if ($recipient->getIsDisabled()) { + $this->throwValidationException( + pht('User is Disabled'), + pht( + 'You can not send welcome mail to this user because their account '. + 'is disabled.')); + } + + if (!$recipient->canEstablishWebSessions()) { + $this->throwValidationException( + pht('Not a Normal User'), + pht( + 'You can not send this user welcome mail because they are not '. + 'a normal user and can not log in to the web interface. Special '. + 'users (like bots and mailing lists) are unable to establish '. + 'web sessions.')); + } + } + + protected function newMail() { + $sender = $this->getSender(); + $recipient = $this->getRecipient(); + + $sender_username = $sender->getUserName(); + $sender_realname = $sender->getRealName(); + + $recipient_username = $recipient->getUserName(); + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); + + $base_uri = PhabricatorEnv::getProductionURI('/'); + + $engine = new PhabricatorAuthSessionEngine(); + + $uri = $engine->getOneTimeLoginURI( + $recipient, + $recipient->loadPrimaryEmail(), + PhabricatorAuthSessionEngine::ONETIME_WELCOME); + + $body = pht( + "Welcome to Phabricator!\n\n". + "%s (%s) has created an account for you.\n\n". + " Username: %s\n\n". + "To login to Phabricator, follow this link and set a password:\n\n". + " %s\n\n". + "After you have set a password, you can login in the future by ". + "going here:\n\n". + " %s\n", + $sender_username, + $sender_realname, + $recipient_username, + $uri, + $base_uri); + + if (!$is_serious) { + $body .= sprintf( + "\n%s\n", + pht("Love,\nPhabricator")); + } + + return id(new PhabricatorMetaMTAMail()) + ->addTos(array($recipient->getPHID())) + ->setSubject(pht('[Phabricator] Welcome to Phabricator')) + ->setBody($body); + } + +} diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -555,56 +555,6 @@ } } - public function sendWelcomeEmail(PhabricatorUser $admin) { - if (!$this->canEstablishWebSessions()) { - throw new Exception( - pht( - 'Can not send welcome mail to users who can not establish '. - 'web sessions!')); - } - - $admin_username = $admin->getUserName(); - $admin_realname = $admin->getRealName(); - $user_username = $this->getUserName(); - $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); - - $base_uri = PhabricatorEnv::getProductionURI('/'); - - $engine = new PhabricatorAuthSessionEngine(); - $uri = $engine->getOneTimeLoginURI( - $this, - $this->loadPrimaryEmail(), - PhabricatorAuthSessionEngine::ONETIME_WELCOME); - - $body = pht( - "Welcome to Phabricator!\n\n". - "%s (%s) has created an account for you.\n\n". - " Username: %s\n\n". - "To login to Phabricator, follow this link and set a password:\n\n". - " %s\n\n". - "After you have set a password, you can login in the future by ". - "going here:\n\n". - " %s\n", - $admin_username, - $admin_realname, - $user_username, - $uri, - $base_uri); - - if (!$is_serious) { - $body .= sprintf( - "\n%s\n", - pht("Love,\nPhabricator")); - } - - $mail = id(new PhabricatorMetaMTAMail()) - ->addTos(array($this->getPHID())) - ->setForceDelivery(true) - ->setSubject(pht('[Phabricator] Welcome to Phabricator')) - ->setBody($body) - ->saveAndSend(); - } - public function sendUsernameChangeEmail( PhabricatorUser $admin, $old_username) {