diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 9f83c2042e..4e001f9964 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -17,7 +17,7 @@ celerity_register_resource_map(array( ), 'aphront-form-view-css' => array( - 'path' => '/res/17285e65/rsrc/css/aphront/form-view.css', + 'path' => '/res/51ec6383/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( @@ -25,7 +25,7 @@ celerity_register_resource_map(array( ), 'aphront-panel-view-css' => array( - 'path' => '/res/d1ce0c3d/rsrc/css/aphront/panel-view.css', + 'path' => '/res/fe62e634/rsrc/css/aphront/panel-view.css', 'type' => 'css', 'requires' => array( @@ -74,7 +74,7 @@ celerity_register_resource_map(array( ), 'differential-changeset-view-css' => array( - 'path' => '/res/921d3a0c/rsrc/css/application/differential/changeset-view.css', + 'path' => '/res/658d181a/rsrc/css/application/differential/changeset-view.css', 'type' => 'css', 'requires' => array( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f3dc42394c..0e23449164 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -19,6 +19,7 @@ phutil_register_library_map(array( 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontFileResponse' => 'aphront/response/file', + 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormMarkupControl' => 'view/form/control/markup', @@ -111,6 +112,11 @@ phutil_register_library_map(array( 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', + 'PhabricatorMetaMTAController' => 'applications/metamta/controller/base', + 'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base', + 'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list', + 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail', + 'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDController' => 'applications/phid/controller/base', @@ -155,6 +161,7 @@ phutil_register_library_map(array( 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', + 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormMarkupControl' => 'AphrontFormControl', @@ -226,6 +233,11 @@ phutil_register_library_map(array( 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorLiskDAO' => 'LiskDAO', + 'PhabricatorMetaMTAController' => 'PhabricatorController', + 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', + 'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController', + 'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO', + 'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index 900a8b320c..faf467307d 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -93,6 +93,12 @@ class AphrontDefaultApplicationConfiguration 'common/(?\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), + + '/mail/' => array( + '$' => 'PhabricatorMetaMTAListController', + 'send/$' => 'PhabricatorMetaMTASendController', + 'view/(?\d+)/$' => 'PhabricatorMetaMTAViewController', + ) ); } diff --git a/src/aphront/request/AphrontRequest.php b/src/aphront/request/AphrontRequest.php index 6d04d61db9..3b8d3a3d86 100644 --- a/src/aphront/request/AphrontRequest.php +++ b/src/aphront/request/AphrontRequest.php @@ -63,7 +63,7 @@ class AphrontRequest { } } - final public function getArr($name, $default = null) { + final public function getArr($name, $default = array()) { if (isset($this->requestData[$name]) && is_array($this->requestData[$name])) { return $this->requestData[$name]; diff --git a/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php b/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php new file mode 100644 index 0000000000..aee09ab14d --- /dev/null +++ b/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php @@ -0,0 +1,34 @@ +setApplicationName('MetaMTA'); + $page->setBaseURI('/mail/'); + $page->setTitle(idx($data, 'title')); + $page->setGlyph("@"); + $page->appendChild($view); + + $response = new AphrontWebpageResponse(); + return $response->setContent($page->render()); + } + +} diff --git a/src/applications/metamta/controller/base/__init__.php b/src/applications/metamta/controller/base/__init__.php new file mode 100644 index 0000000000..23abc13fab --- /dev/null +++ b/src/applications/metamta/controller/base/__init__.php @@ -0,0 +1,16 @@ +loadAllWhere( + '1 = 1 ORDER BY id DESC LIMIT 100'); + + $rows = array(); + foreach ($mails as $mail) { + $rows[] = array( + $mail->getID(), + ); + } + + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + 'ID', + )); + $table->setColumnClasses( + array( + null, + )); + + $panel = new AphrontPanelView(); + $panel->appendChild($table); + $panel->setHeader('MetaMTA Messages'); + $panel->setCreateButton('Send New Message', '/mail/send/'); + + return $this->buildStandardPageResponse( + $panel, + array( + 'title' => 'MetaMTA', + )); + } +} diff --git a/src/applications/metamta/controller/list/__init__.php b/src/applications/metamta/controller/list/__init__.php new file mode 100644 index 0000000000..520ca62a9d --- /dev/null +++ b/src/applications/metamta/controller/list/__init__.php @@ -0,0 +1,17 @@ +getRequest(); + + if ($request->isFormPost()) { + $mail = new PhabricatorMetaMTAMail(); + $mail->addTos($request->getArr('to')); + $mail->addCCs($request->getArr('cc')); + $mail->setSubject($request->getStr('subject')); + $mail->setBody($request->getStr('body')); + + // TODO! +// $mail->setFrom($request->getViewerContext()->getUserID()); + $mail->setSimulatedFailureCount($request->getInt('failures')); + $mail->setIsHTML($request->getInt('html')); + $mail->save(); + if ($request->getInt('immediately')) { + $mail->sendNow($force_send = true); + } + + return id(new AphrontRedirectResponse()) + ->setURI('/mail/view/'.$mail->getID().'/'); + } + + $failure_caption = + "Enter a number to simulate that many consecutive send failures before ". + "really attempting to deliver via the underlying MTA."; + + + $form = new AphrontFormView(); + $form->setAction('/mail/send/'); + $form + ->appendChild( + '

This form will send a normal '. + 'email using MetaMTA as a transport mechanism.

') + ->appendChild( + id(new AphrontFormTokenizerControl()) + ->setLabel('To') + ->setName('to') + ->setDatasource('/typeahead/common/user/')) + ->appendChild( + id(new AphrontFormTokenizerControl()) + ->setLabel('CC') + ->setName('cc') + ->setDatasource('/typeahead/common/user/')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Subject') + ->setName('subject')) + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setLabel('Body') + ->setName('body')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Simulate Failures') + ->setName('failures') + ->setCaption($failure_caption)) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->setLabel('HTML') + ->addCheckbox('html', '1', 'Send as HTML email.')) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->setLabel('Send Now') + ->addCheckbox( + 'immediately', + '1', + 'Send immediately, not via MetaMTA background script.')) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Send Mail')); + + $panel = new AphrontPanelView(); + $panel->setHeader('Send Email'); + $panel->appendChild($form); + $panel->setWidth(AphrontPanelView::WIDTH_WIDE); + + return $this->buildStandardPageResponse( + $panel, + array( + 'title' => 'Send Mail', + )); +/* + return + + +

Send Email

+ + This form will send a normal email using MetaMTA + as a transport mechanism. + + + + + + + + + + + + + + + + + + Send as HTML email. + + + + Send immediately, not via MetaMTA daemon. + + + + + +
+
; +*/ + } + +} diff --git a/src/applications/metamta/controller/send/__init__.php b/src/applications/metamta/controller/send/__init__.php new file mode 100644 index 0000000000..d606f64d0f --- /dev/null +++ b/src/applications/metamta/controller/send/__init__.php @@ -0,0 +1,19 @@ +status = self::STATUS_QUEUE; + $this->retryCount = 0; + $this->nextRetry = time(); + $this->parameters = array(); + + parent::__construct(); + } + + public function getConfiguration() { + return array( + self::CONFIG_SERIALIZATION => array( + 'parameters' => self::SERIALIZATION_JSON, + ), + ) + parent::getConfiguration(); + } + + protected function setParam($param, $value) { + $this->parameters[$param] = $value; + return $this; + } + + protected function getParam($param) { + return idx($this->parameters, $param); + } + + public function getSubject() { + return $this->getParam('subject'); + } + + public function addTos(array $phids) { + $this->setParam('to', $phids); + return $this; + } + + public function addCCs(array $phids) { + $this->setParam('cc', $phids); + return $this; + } + + public function addHeader($name, $value) { + $this->parameters['headers'][$name] = $value; + return $this; + } + + public function setFrom($from) { + $this->setParam('from', $from); + return $this; + } + + public function setReplyTo($phid) { + $this->setParam('reply-to', $phid); + return $this; + } + + public function setSubject($subject) { + $this->setParam('subject', $subject); + return $this; + } + + public function setBody($body) { + $this->setParam('body', $body); + return $this; + } + + public function setIsHTML($html) { + $this->setParam('is-html', $html); + return $this; + } + + public function getSimulatedFailureCount() { + return nonempty($this->getParam('simulated-failures'), 0); + } + + public function setSimulatedFailureCount($count) { + $this->setParam('simulated-failures', $count); + return $this; + } + + public function sendNow($force_send = false) { + +/* + if (!$force_send) { + if ($this->getStatus() != self::STATUS_QUEUE) { + throw new Exception("Trying to send an already-sent mail!"); + } + + if (time() < $this->getNextRetry()) { + throw new Exception("Trying to send an email before next retry!"); + } + } + + require_module_lazy('intern/mailer'); + try { + $mailer = new InternMailer(); + foreach ($this->parameters as $key => $value) { + switch ($key) { + case 'from': + $mailer->setFrom($value); + break; + case 'reply-to': + $mailer->addReplyTo($value); + break; + case 'to': + $mailer->addTos($value); + break; + case 'cc': + $mailer->addCCs($value); + break; + case 'headers': + foreach ($value as $header_key => $header_value) { + $mailer->addHeader($header_key, $header_value); + } + break; + case 'body': + $mailer->setBody($value); + break; + case 'subject': + $mailer->setSubject($value); + break; + case 'is-html': + if ($value) { + $mailer->setIsHTML(true); + } + break; + default: + // Just discard. + } + } + + $mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA'); + + if (!$mailer->hasValidRecipients()) { + throw new Exception("Permanent failure: no valid recipients."); + } + } catch (Exception $ex) { + $this->setStatus(self::STATUS_FAIL); + $this->setMessage($ex->getMessage()); + $this->save(); + return; + } + + if ($this->getRetryCount() < $this->getSimulatedFailureCount()) { + $ok = false; + $error = 'Simulated failure.'; + } else { + $ok = $mailer->send(); + $error = $mailer->getError(); + } +*/ + $error = null; + $ok = true; + + if (!$ok) { + $this->setMessage($error); + if ($this->getRetryCount() > self::MAX_RETRIES) { + $this->setStatus(self::STATUS_FAIL); + } else { + $this->setRetryCount($this->getRetryCount() + 1); + $next_retry = time() + ($this->getRetryCount() * self::RETRY_DELAY); + $this->setNextRetry($next_retry); + } + } else { + $this->setStatus(self::STATUS_SENT); + } + + $this->save(); + } + + public static function getReadableStatus($status_code) { + static $readable = array( + self::STATUS_QUEUE => "Queued for Delivery", + self::STATUS_FAIL => "Delivery Failed", + self::STATUS_SENT => "Sent", + ); + $status_code = coalesce($status_code, '?'); + return idx($readable, $status_code, $status_code); + } + +} diff --git a/src/applications/metamta/storage/mail/__init__.php b/src/applications/metamta/storage/mail/__init__.php new file mode 100644 index 0000000000..43e0257082 --- /dev/null +++ b/src/applications/metamta/storage/mail/__init__.php @@ -0,0 +1,14 @@ +getLabel())) { $label = - '