1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-26 21:49:08 +01:00

MetaMTA - add (basic) application emails and deploy to Maniphest

Summary: Ref T5952, T3404. This lays the basic plumbing for how this will work, all the way to deploying on Maniphest. Aside from what is mentioned on T5952, I think page(s) on editing application emails could use a little more helpful text about what's going on, similar to how the config page that's getting deprecated works.

Test Plan: ran migration and noted my create email address migrated successfully. used bin/mail to make a task. added another email and used bin/mail to make a task. deleted an email. edited an email. invoked various error states and they all looked good.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T3404, T5952

Differential Revision: https://secure.phabricator.com/D11418
This commit is contained in:
Bob Trahan 2015-01-19 16:07:26 -08:00
parent a15852c112
commit 53b06408f4
15 changed files with 695 additions and 21 deletions

View file

@ -0,0 +1,12 @@
CREATE TABLE {$NAMESPACE}_metamta.metamta_applicationemail (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
applicationPHID VARBINARY(64) NOT NULL,
address VARCHAR(128) NOT NULL COLLATE {$COLLATE_SORT},
configData LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
KEY `key_application` (applicationPHID),
UNIQUE KEY `key_address` (address),
UNIQUE KEY `key_phid` (phid)
) ENGINE=MyISAM DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT};

View file

@ -0,0 +1,20 @@
<?php
$key = 'metamta.maniphest.public-create-email';
echo "Migrating `$key` to new application email infrastructure...\n";
$value = PhabricatorEnv::getEnvConfigIfExists($key);
$maniphest = new PhabricatorManiphestApplication();
if ($value) {
try {
PhabricatorMetaMTAApplicationEmail::initializeNewAppEmail(
PhabricatorUser::getOmnipotentUser())
->setAddress($value)
->setApplicationPHID($maniphest->getPHID())
->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// already migrated?
}
}
echo "Done.\n";

View file

@ -1258,6 +1258,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php',
'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php',
'PhabricatorApplicationEditEmailController' => 'applications/meta/controller/PhabricatorApplicationEditEmailController.php',
'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php',
'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php', 'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php',
'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php', 'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php',
@ -1946,6 +1947,9 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAActor' => 'applications/metamta/query/PhabricatorMetaMTAActor.php', 'PhabricatorMetaMTAActor' => 'applications/metamta/query/PhabricatorMetaMTAActor.php',
'PhabricatorMetaMTAActorQuery' => 'applications/metamta/query/PhabricatorMetaMTAActorQuery.php', 'PhabricatorMetaMTAActorQuery' => 'applications/metamta/query/PhabricatorMetaMTAActorQuery.php',
'PhabricatorMetaMTAApplication' => 'applications/metamta/application/PhabricatorMetaMTAApplication.php', 'PhabricatorMetaMTAApplication' => 'applications/metamta/application/PhabricatorMetaMTAApplication.php',
'PhabricatorMetaMTAApplicationEmail' => 'applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php',
'PhabricatorMetaMTAApplicationEmailPHIDType' => 'applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php',
'PhabricatorMetaMTAApplicationEmailQuery' => 'applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php',
'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php', 'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php',
'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php', 'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php',
'PhabricatorMetaMTAController' => 'applications/metamta/controller/PhabricatorMetaMTAController.php', 'PhabricatorMetaMTAController' => 'applications/metamta/controller/PhabricatorMetaMTAController.php',
@ -4430,6 +4434,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationEditEmailController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationLaunchView' => 'AphrontTagView', 'PhabricatorApplicationLaunchView' => 'AphrontTagView',
'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController', 'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
@ -5155,6 +5160,12 @@ phutil_register_library_map(array(
'PhabricatorMercurialGraphStream' => 'PhabricatorRepositoryGraphStream', 'PhabricatorMercurialGraphStream' => 'PhabricatorRepositoryGraphStream',
'PhabricatorMetaMTAActorQuery' => 'PhabricatorQuery', 'PhabricatorMetaMTAActorQuery' => 'PhabricatorQuery',
'PhabricatorMetaMTAApplication' => 'PhabricatorApplication', 'PhabricatorMetaMTAApplication' => 'PhabricatorApplication',
'PhabricatorMetaMTAApplicationEmail' => array(
'PhabricatorMetaMTADAO',
'PhabricatorPolicyInterface',
),
'PhabricatorMetaMTAApplicationEmailPHIDType' => 'PhabricatorPHIDType',
'PhabricatorMetaMTAApplicationEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorMetaMTAController' => 'PhabricatorController', 'PhabricatorMetaMTAController' => 'PhabricatorController',
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',

View file

@ -4,6 +4,7 @@
* @task info Application Information * @task info Application Information
* @task ui UI Integration * @task ui UI Integration
* @task uri URI Routing * @task uri URI Routing
* @task mail Email integration
* @task fact Fact Integration * @task fact Fact Integration
* @task meta Application Management * @task meta Application Management
*/ */
@ -193,6 +194,22 @@ abstract class PhabricatorApplication implements PhabricatorPolicyInterface {
} }
/* -( Email Integration )-------------------------------------------------- */
public function supportsEmailIntegration() {
return false;
}
protected function getInboundEmailSupportLink() {
return PhabricatorEnv::getDocLink('Configuring Inbound Email');
}
public function getAppEmailBlurb() {
throw new Exception('Not Implemented.');
}
/* -( Fact Integration )--------------------------------------------------- */ /* -( Fact Integration )--------------------------------------------------- */

View file

@ -109,6 +109,20 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
return $items; return $items;
} }
public function supportsEmailIntegration() {
return true;
}
public function getAppEmailBlurb() {
return pht(
'Send email to these addresses to create tasks. %s',
phutil_tag(
'a',
array(
'href' => $this->getInboundEmailSupportLink(),),
pht('Learn More')));
}
protected function getCustomCapabilities() { protected function getCustomCapabilities() {
return array( return array(
ManiphestDefaultViewCapability::CAPABILITY => array( ManiphestDefaultViewCapability::CAPABILITY => array(

View file

@ -294,9 +294,16 @@ EOTEXT
'metamta.maniphest.public-create-email', 'metamta.maniphest.public-create-email',
'string', 'string',
null) null)
->setSummary(pht('Allow filing bugs via email.')) ->setLocked(true)
->setLockedMessage(pht(
'This configuration is deprecated. See description for details.'))
->setSummary(pht('DEPRECATED - Allow filing bugs via email.'))
->setDescription( ->setDescription(
pht( pht(
'This config has been deprecated in favor of [[ '.
'/applications/view/PhabricatorManiphestApplication/ | '.
'application settings ]], which allow for multiple email '.
'addresses and other functionality.'."\n\n".
'You can configure an email address like '. 'You can configure an email address like '.
'"bugs@phabricator.example.com" which will automatically create '. '"bugs@phabricator.example.com" which will automatically create '.
'Maniphest tasks when users send email to it. This relies on the '. 'Maniphest tasks when users send email to it. This relies on the '.

View file

@ -8,12 +8,18 @@ final class ManiphestCreateMailReceiver extends PhabricatorMailReceiver {
} }
public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) { public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail) {
$config_key = 'metamta.maniphest.public-create-email'; $maniphest_app = new PhabricatorManiphestApplication();
$create_address = PhabricatorEnv::getEnvConfig($config_key); $application_emails = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($this->getViewer())
->withApplicationPHIDs(array($maniphest_app->getPHID()))
->execute();
foreach ($mail->getToAddresses() as $to_address) { foreach ($mail->getToAddresses() as $to_address) {
if ($this->matchAddresses($create_address, $to_address)) { foreach ($application_emails as $application_email) {
return true; $create_address = $application_email->getAddress();
if ($this->matchAddresses($create_address, $to_address)) {
return true;
}
} }
} }

View file

@ -41,6 +41,8 @@ final class PhabricatorApplicationsApplication extends PhabricatorApplication {
=> 'PhabricatorApplicationDetailViewController', => 'PhabricatorApplicationDetailViewController',
'edit/(?P<application>\w+)/' 'edit/(?P<application>\w+)/'
=> 'PhabricatorApplicationEditController', => 'PhabricatorApplicationEditController',
'editemail/(?P<application>\w+)/'
=> 'PhabricatorApplicationEditEmailController',
'(?P<application>\w+)/(?P<action>install|uninstall)/' '(?P<application>\w+)/(?P<action>install|uninstall)/'
=> 'PhabricatorApplicationUninstallController', => 'PhabricatorApplicationUninstallController',
), ),

View file

@ -3,23 +3,18 @@
final class PhabricatorApplicationDetailViewController final class PhabricatorApplicationDetailViewController
extends PhabricatorApplicationsController { extends PhabricatorApplicationsController {
private $application;
public function shouldAllowPublic() { public function shouldAllowPublic() {
return true; return true;
} }
public function willProcessRequest(array $data) { public function handleRequest(AphrontRequest $request) {
$this->application = $data['application'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$application = $request->getURIData('application');
$selected = id(new PhabricatorApplicationQuery()) $selected = id(new PhabricatorApplicationQuery())
->setViewer($user) ->setViewer($user)
->withClasses(array($this->application)) ->withClasses(array($application))
->executeOne(); ->executeOne();
if (!$selected) { if (!$selected) {
return new Aphront404Response(); return new Aphront404Response();
@ -119,6 +114,26 @@ final class PhabricatorApplicationDetailViewController
idx($descriptions, $capability)); idx($descriptions, $capability));
} }
if ($application->supportsEmailIntegration()) {
$properties->addSectionHeader(pht('Application Emails'));
$properties->addTextContent($application->getAppEmailBlurb());
$email_addresses = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withApplicationPHIDs(array($application->getPHID()))
->execute();
if (empty($email_addresses)) {
$properties->addProperty(
null,
pht('No email addresses configured.'));
} else {
foreach ($email_addresses as $email_address) {
$properties->addProperty(
null,
$email_address->getAddress());
}
}
}
return $properties; return $properties;
} }
@ -153,6 +168,18 @@ final class PhabricatorApplicationDetailViewController
->setWorkflow(!$can_edit) ->setWorkflow(!$can_edit)
->setHref($edit_uri)); ->setHref($edit_uri));
if ($selected->supportsEmailIntegration()) {
$edit_email_uri = $this->getApplicationURI(
'editemail/'.get_class($selected).'/');
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Application Emails'))
->setIcon('fa-envelope')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($edit_email_uri));
}
if ($selected->canUninstall()) { if ($selected->canUninstall()) {
if ($selected->isInstalled()) { if ($selected->isInstalled()) {
$view->addAction( $view->addAction(

View file

@ -0,0 +1,307 @@
<?php
final class PhabricatorApplicationEditEmailController
extends PhabricatorApplicationsController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$application = $request->getURIData('application');
$application = id(new PhabricatorApplicationQuery())
->setViewer($viewer)
->withClasses(array($application))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$application) {
return new Aphront404Response();
}
$title = $application->getName();
$uri = $request->getRequestURI();
$uri->setQueryParams(array());
$new = $request->getStr('new');
$edit = $request->getInt('edit');
$delete = $request->getInt('delete');
if ($new) {
return $this->returnNewAddressResponse($request, $uri, $application);
}
if ($edit) {
return $this->returnEditAddressResponse($request, $uri, $edit);
}
if ($delete) {
return $this->returnDeleteAddressResponse($request, $uri, $delete);
}
$emails = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withApplicationPHIDs(array($application->getPHID()))
->execute();
$highlight = $request->getInt('highlight');
$rowc = array();
$rows = array();
foreach ($emails as $email) {
$button_edit = javelin_tag(
'a',
array(
'class' => 'button small grey',
'href' => $uri->alter('edit', $email->getID()),
'sigil' => 'workflow',
),
pht('Edit'));
$button_remove = javelin_tag(
'a',
array(
'class' => 'button small grey',
'href' => $uri->alter('delete', $email->getID()),
'sigil' => 'workflow',
),
pht('Delete'));
if ($highlight == $email->getID()) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$rows[] = array(
$email->getAddress(),
$button_edit,
$button_remove,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No application emails created yet.'));
$table->setHeaders(
array(
pht('Email'),
pht('Edit'),
pht('Delete'),
));
$table->setColumnClasses(
array(
'wide',
'action',
'action',
));
$table->setRowClasses($rowc);
$table->setColumnVisibility(
array(
true,
true,
true,
));
$form = id(new AphrontFormView())
->setUser($viewer);
$view_uri = $this->getApplicationURI('view/'.get_class($application).'/');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($application->getName(), $view_uri);
$crumbs->addTextCrumb(pht('Edit Application Emails'));
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Application Emails: %s', $application->getName()));
$icon = id(new PHUIIconView())
->setIconFont('fa-plus');
$button = new PHUIButtonView();
$button->setText(pht('Add New Address'));
$button->setTag('a');
$button->setHref($uri->alter('new', 'true'));
$button->setIcon($icon);
$button->addSigil('workflow');
$header->addActionLink($button);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table)
->appendChild(
id(new PHUIBoxView())
->appendChild($application->getAppEmailBlurb())
->addPadding(PHUI::PADDING_MEDIUM));
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
),
array(
'title' => $title,
));
}
private function validateApplicationEmail($email) {
$errors = array();
$e_email = true;
if (!strlen($email)) {
$e_email = pht('Required');
$errors[] = pht('Email is required.');
} else if (!PhabricatorUserEmail::isValidAddress($email)) {
$e_email = pht('Invalid');
$errors[] = PhabricatorUserEmail::describeValidAddresses();
} else if (!PhabricatorUserEmail::isAllowedAddress($email)) {
$e_email = pht('Disallowed');
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
}
$user_emails = id(new PhabricatorUserEmail())
->loadAllWhere('address = %s', $email);
if ($user_emails) {
$e_email = pht('Duplicate');
$errors[] = pht('A user already has this email.');
}
return array($e_email, $errors);
}
private function returnNewAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
PhabricatorApplication $application) {
$viewer = $request->getUser();
$email_object =
PhabricatorMetaMTAApplicationEmail::initializeNewAppEmail($viewer)
->setApplicationPHID($application->getPHID());
return $this->returnSaveAddressResponse(
$request,
$uri,
$email_object,
$is_new = true);
}
private function returnEditAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_object_id) {
$viewer = $request->getUser();
$email_object = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withIDs(array($email_object_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$email_object) {
return new Aphront404Response();
}
return $this->returnSaveAddressResponse(
$request,
$uri,
$email_object,
$is_new = false);
}
private function returnSaveAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
PhabricatorMetaMTAApplicationEmail $email_object,
$is_new) {
$viewer = $request->getUser();
$e_email = true;
$email = null;
$errors = array();
if ($request->isDialogFormPost()) {
$email = trim($request->getStr('email'));
list($e_email, $errors) = $this->validateApplicationEmail($email);
$email_object->setAddress($email);
if (!$errors) {
try {
$email_object->save();
return id(new AphrontRedirectResponse())->setURI(
$uri->alter('highlight', $email_object->getID()));
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_email = pht('Duplicate');
$errors[] = pht(
'Another application is already configured to use this email '.
'address.');
}
}
}
if ($errors) {
$errors = id(new AphrontErrorView())
->setErrors($errors);
}
$form = id(new PHUIFormLayoutView())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setName('email')
->setValue($email_object->getAddress())
->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
->setError($e_email));
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('New Address'))
->appendChild($errors)
->appendChild($form)
->addSubmitButton(pht('Save'))
->addCancelButton($uri);
if ($is_new) {
$dialog->addHiddenInput('new', 'true');
}
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function returnDeleteAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_object_id) {
$viewer = $request->getUser();
$email_object = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($viewer)
->withIDs(array($email_object_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$email_object) {
return new Aphront404Response();
}
if ($request->isDialogFormPost()) {
$email_object->delete();
return id(new AphrontRedirectResponse())->setURI($uri);
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('delete', $email_object_id)
->setTitle(pht('Delete Address'))
->appendParagraph(pht(
'Are you sure you want to delete this email address?'))
->addSubmitButton(pht('Delete'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,116 @@
<?php
final class PhabricatorMetaMTAApplicationEmailQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $addresses;
private $applicationPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withAddresses(array $addresses) {
$this->addresses = $addresses;
return $this;
}
public function withApplicationPHIDs(array $phids) {
$this->applicationPHIDs = $phids;
return $this;
}
protected function loadPage() {
$table = new PhabricatorMetaMTAApplicationEmail();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T appemail %Q %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildApplicationSearchGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $app_emails) {
$app_emails_map = mgroup($app_emails, 'getApplicationPHID');
$applications = id(new PhabricatorApplicationQuery())
->setViewer($this->getViewer())
->withPHIDs(array_keys($app_emails_map))
->execute();
$applications = mpull($applications, null, 'getPHID');
foreach ($app_emails_map as $app_phid => $app_emails_group) {
foreach ($app_emails_group as $app_email) {
$application = idx($applications, $app_phid);
if (!$application) {
unset($app_emails[$app_phid]);
continue;
}
$app_email->attachApplication($application);
}
}
return $app_emails;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->addresses !== null) {
$where[] = qsprintf(
$conn_r,
'appemail.address IN (%Ls)',
$this->addresses);
}
if ($this->applicationPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'appemail.applicationPHID IN (%Ls)',
$this->applicationPHIDs);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'appemail.phid IN (%Ls)',
$this->phids);
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'appemail.id IN (%Ld)',
$this->ids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
return 'appemail.id';
}
protected function getApplicationSearchObjectPHIDColumn() {
return 'appemail.phid';
}
public function getQueryApplicationClass() {
return 'PhabricatorMetaMTAApplication';
}
}

View file

@ -15,6 +15,10 @@ abstract class PhabricatorMailReceiver {
$this->processReceivedMail($mail, $sender); $this->processReceivedMail($mail, $sender);
} }
public function getViewer() {
return PhabricatorUser::getOmnipotentUser();
}
public function validateSender( public function validateSender(
PhabricatorMetaMTAReceivedMail $mail, PhabricatorMetaMTAReceivedMail $mail,
PhabricatorUser $sender) { PhabricatorUser $sender) {
@ -103,7 +107,7 @@ abstract class PhabricatorMailReceiver {
if ($allow_email_users) { if ($allow_email_users) {
$from_obj = new PhutilEmailAddress($from); $from_obj = new PhutilEmailAddress($from);
$xuser = id(new PhabricatorExternalAccountQuery()) $xuser = id(new PhabricatorExternalAccountQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer($this->getViewer())
->withAccountTypes(array('email')) ->withAccountTypes(array('email'))
->withAccountDomains(array($from_obj->getDomainName(), 'self')) ->withAccountDomains(array($from_obj->getDomainName(), 'self'))
->withAccountIDs(array($from_obj->getAddress())) ->withAccountIDs(array($from_obj->getAddress()))

View file

@ -0,0 +1,80 @@
<?php
final class PhabricatorMetaMTAApplicationEmail
extends PhabricatorMetaMTADAO
implements PhabricatorPolicyInterface {
protected $applicationPHID;
protected $address;
protected $configData;
private $application = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'configData' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'address' => 'sort128',
),
self::CONFIG_KEY_SCHEMA => array(
'key_address' => array(
'columns' => array('address'),
'unique' => true,
),
'key_application' => array(
'columns' => array('applicationPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorMetaMTAApplicationEmailPHIDType::TYPECONST);
}
public static function initializeNewAppEmail(PhabricatorUser $actor) {
return id(new PhabricatorMetaMTAApplicationEmail())
->setConfigData(array());
}
public function attachApplication(PhabricatorApplication $app) {
$this->application = $app;
return $this;
}
public function getApplication() {
return self::assertAttached($this->application);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getApplication()->getPolicy($capability);
}
public function hasAutomaticCapability(
$capability,
PhabricatorUser $viewer) {
return $this->getApplication()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return $this->getApplication()->describeAutomaticCapability($capability);
}
}

View file

@ -64,10 +64,9 @@ final class PhabricatorMetaMTAReceivedMailTestCase extends PhabricatorTestCase {
} }
public function testDropUnknownSenderMail() { public function testDropUnknownSenderMail() {
$this->setManiphestCreateEmail();
$env = PhabricatorEnv::beginScopedEnv(); $env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig(
'metamta.maniphest.public-create-email',
'bugs@example.com');
$env->overrideEnvConfig('phabricator.allow-email-users', false); $env->overrideEnvConfig('phabricator.allow-email-users', false);
$env->overrideEnvConfig('metamta.maniphest.default-public-author', null); $env->overrideEnvConfig('metamta.maniphest.default-public-author', null);
@ -89,10 +88,7 @@ final class PhabricatorMetaMTAReceivedMailTestCase extends PhabricatorTestCase {
public function testDropDisabledSenderMail() { public function testDropDisabledSenderMail() {
$env = PhabricatorEnv::beginScopedEnv(); $this->setManiphestCreateEmail();
$env->overrideEnvConfig(
'metamta.maniphest.public-create-email',
'bugs@example.com');
$user = $this->generateNewTestUser() $user = $this->generateNewTestUser()
->setIsDisabled(true) ->setIsDisabled(true)
@ -114,4 +110,15 @@ final class PhabricatorMetaMTAReceivedMailTestCase extends PhabricatorTestCase {
$mail->getStatus()); $mail->getStatus());
} }
private function setManiphestCreateEmail() {
$maniphest_app = new PhabricatorManiphestApplication();
try {
id(new PhabricatorMetaMTAApplicationEmail())
->setApplicationPHID($maniphest_app->getPHID())
->setAddress('bugs@example.com')
->setConfigData(array())
->save();
} catch (AphrontDuplicateKeyQueryException $ex) {}
}
} }

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorMetaMTAApplicationEmailPHIDType
extends PhabricatorPHIDType {
const TYPECONST = 'APPE';
public function getTypeName() {
return pht('Application Email');
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorMetaMTAApplication';
}
public function getTypeIcon() {
return 'fa-email bluegrey';
}
public function newObject() {
return new PhabricatorMetaMTAApplicationEmail();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorMetaMTAApplicationEmailQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$email = $objects[$phid];
$handle->setName($email->getAddress());
$handle->setFullName($email->getAddress());
}
}
}