From 9d9e3afba2054bfa23ba6f019b7c8885c2d8415e Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 6 Jun 2015 14:10:31 -0400 Subject: Improve email sending system and add Postmark as mail transport --- app/Controller/Webhook.php | 2 +- app/Core/Base.php | 4 +- app/Core/EmailClient.php | 43 +++++++++++++ app/Core/HttpClient.php | 51 +++++++--------- app/Integration/Postmark.php | 97 ++++++++++++++++++++++++++++++ app/Integration/PostmarkWebhook.php | 70 ---------------------- app/Integration/Smtp.php | 71 ++++++++++++++++++++++ app/Model/Notification.php | 74 ++++++++--------------- app/ServiceProvider/ClassProvider.php | 4 +- app/ServiceProvider/MailerProvider.php | 33 ---------- app/common.php | 1 - app/constants.php | 1 + config.default.php | 5 +- docs/email-configuration.markdown | 38 ++++++++++++ tests/units/Base.php | 10 +++- tests/units/PostmarkTest.php | 106 +++++++++++++++++++++++++++++++++ tests/units/PostmarkWebhookTest.php | 83 -------------------------- 17 files changed, 420 insertions(+), 273 deletions(-) create mode 100644 app/Core/EmailClient.php create mode 100644 app/Integration/Postmark.php delete mode 100644 app/Integration/PostmarkWebhook.php create mode 100644 app/Integration/Smtp.php delete mode 100644 app/ServiceProvider/MailerProvider.php create mode 100644 tests/units/PostmarkTest.php delete mode 100644 tests/units/PostmarkWebhookTest.php diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php index c79b4ed6..ddbf7d62 100644 --- a/app/Controller/Webhook.php +++ b/app/Controller/Webhook.php @@ -112,7 +112,7 @@ class Webhook extends Base $this->response->text('Not Authorized', 401); } - echo $this->postmarkWebhook->parsePayload($this->request->getJson() ?: array()) ? 'PARSED' : 'IGNORED'; + echo $this->postmark->receiveEmail($this->request->getJson() ?: array()) ? 'PARSED' : 'IGNORED'; } /** diff --git a/app/Core/Base.php b/app/Core/Base.php index cb8e4487..5e179b13 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -11,6 +11,7 @@ use Pimple\Container; * @author Frederic Guillot * * @property \Core\Helper $helper + * @property \Core\EmailClient $emailClient * @property \Core\HttpClient $httpClient * @property \Core\Paginator $paginator * @property \Core\Request $request @@ -22,9 +23,10 @@ use Pimple\Container; * @property \Integration\HipchatWebhook $hipchatWebhook * @property \Integration\Jabber $jabber * @property \Integration\MailgunWebhook $mailgunWebhook - * @property \Integration\PostmarkWebhook $postmarkWebhook + * @property \Integration\Postmark $postmark * @property \Integration\SendgridWebhook $sendgridWebhook * @property \Integration\SlackWebhook $slackWebhook + * @property \Integration\Smtp $smtp * @property \Model\Acl $acl * @property \Model\Action $action * @property \Model\Authentication $authentication diff --git a/app/Core/EmailClient.php b/app/Core/EmailClient.php new file mode 100644 index 00000000..980f5acc --- /dev/null +++ b/app/Core/EmailClient.php @@ -0,0 +1,43 @@ +container['logger']->debug('Sending email to '.$email.' ('.MAIL_TRANSPORT.')'); + + $start_time = microtime(true); + $author = 'Kanboard'; + + if (Session::isOpen() && $this->userSession->isLogged()) { + $author = e('%s via Kanboard', $this->user->getFullname($this->session['user'])); + } + + switch (MAIL_TRANSPORT) { + case 'postmark': + $this->postmark->sendEmail($email, $name, $subject, $html, $author); + break; + default: + $this->smtp->sendEmail($email, $name, $subject, $html, $author); + } + + $this->container['logger']->debug('Email sent in '.round(microtime(true) - $start_time, 6).' seconds'); + } +} diff --git a/app/Core/HttpClient.php b/app/Core/HttpClient.php index 0803ec7a..fcfb1a47 100644 --- a/app/Core/HttpClient.php +++ b/app/Core/HttpClient.php @@ -2,22 +2,20 @@ namespace Core; -use Pimple\Container; - /** * HTTP client * * @package core * @author Frederic Guillot */ -class HttpClient +class HttpClient extends Base { /** * HTTP connection timeout in seconds * * @var integer */ - const HTTP_TIMEOUT = 2; + const HTTP_TIMEOUT = 5; /** * Number of maximum redirections for the HTTP client @@ -31,26 +29,7 @@ class HttpClient * * @var string */ - const HTTP_USER_AGENT = 'Kanboard Webhook'; - - /** - * Container instance - * - * @access protected - * @var \Pimple\Container - */ - protected $container; - - /** - * Constructor - * - * @access public - * @param \Pimple\Container $container - */ - public function __construct(Container $container) - { - $this->container = $container; - } + const HTTP_USER_AGENT = 'Kanboard'; /** * Send a POST HTTP request @@ -58,19 +37,20 @@ class HttpClient * @access public * @param string $url * @param array $data + * @param array $headers * @return string */ - public function post($url, array $data) + public function post($url, array $data, array $headers = array()) { if (empty($url)) { return ''; } - $headers = array( + $headers = array_merge(array( 'User-Agent: '.self::HTTP_USER_AGENT, 'Content-Type: application/json', 'Connection: close', - ); + ), $headers); $context = stream_context_create(array( 'http' => array( @@ -83,12 +63,21 @@ class HttpClient ) )); - $response = @file_get_contents(trim($url), false, $context); + $stream = @fopen(trim($url), 'r', false, $context); + $response = ''; + + if (is_resource($stream)) { + $response = stream_get_contents($stream); + } + else { + $this->container['logger']->error('HttpClient: request failed'); + } if (DEBUG) { - $this->container['logger']->debug($url); - $this->container['logger']->debug(var_export($data, true)); - $this->container['logger']->debug($response); + $this->container['logger']->debug('HttpClient: url='.$url); + $this->container['logger']->debug('HttpClient: payload='.var_export($data, true)); + $this->container['logger']->debug('HttpClient: metadata='.var_export(@stream_get_meta_data($stream), true)); + $this->container['logger']->debug('HttpClient: response='.$response); } return $response; diff --git a/app/Integration/Postmark.php b/app/Integration/Postmark.php new file mode 100644 index 00000000..a367c23e --- /dev/null +++ b/app/Integration/Postmark.php @@ -0,0 +1,97 @@ + sprintf('%s <%s>', $author, MAIL_FROM), + 'To' => sprintf('%s <%s>', $name, $email), + 'Subject' => $subject, + 'HtmlBody' => $html, + ); + + $this->httpClient->post('https://api.postmarkapp.com/email', $payload, $headers); + } + + /** + * Parse incoming email + * + * @access public + * @param array $payload Incoming email + * @return boolean + */ + public function receiveEmail(array $payload) + { + if (empty($payload['From']) || empty($payload['Subject']) || empty($payload['MailboxHash'])) { + return false; + } + + // The user must exists in Kanboard + $user = $this->user->getByEmail($payload['From']); + + if (empty($user)) { + $this->container['logger']->debug('Postmark: ignored => user not found'); + return false; + } + + // The project must have a short name + $project = $this->project->getByIdentifier($payload['MailboxHash']); + + if (empty($project)) { + $this->container['logger']->debug('Postmark: ignored => project not found'); + return false; + } + + // The user must be member of the project + if (! $this->projectPermission->isMember($project['id'], $user['id'])) { + $this->container['logger']->debug('Postmark: ignored => user is not member of the project'); + return false; + } + + // Get the Markdown contents + if (! empty($payload['HtmlBody'])) { + $markdown = new HTML_To_Markdown($payload['HtmlBody'], array('strip_tags' => true)); + $description = $markdown->output(); + } + else if (! empty($payload['TextBody'])) { + $description = $payload['TextBody']; + } + else { + $description = ''; + } + + // Finally, we create the task + return (bool) $this->taskCreation->create(array( + 'project_id' => $project['id'], + 'title' => $payload['Subject'], + 'description' => $description, + 'creator_id' => $user['id'], + )); + } +} diff --git a/app/Integration/PostmarkWebhook.php b/app/Integration/PostmarkWebhook.php deleted file mode 100644 index 9051e5f7..00000000 --- a/app/Integration/PostmarkWebhook.php +++ /dev/null @@ -1,70 +0,0 @@ -user->getByEmail($payload['From']); - - if (empty($user)) { - $this->container['logger']->debug('PostmarkWebhook: ignored => user not found'); - return false; - } - - // The project must have a short name - $project = $this->project->getByIdentifier($payload['MailboxHash']); - - if (empty($project)) { - $this->container['logger']->debug('PostmarkWebhook: ignored => project not found'); - return false; - } - - // The user must be member of the project - if (! $this->projectPermission->isMember($project['id'], $user['id'])) { - $this->container['logger']->debug('PostmarkWebhook: ignored => user is not member of the project'); - return false; - } - - // Get the Markdown contents - if (! empty($payload['HtmlBody'])) { - $markdown = new HTML_To_Markdown($payload['HtmlBody'], array('strip_tags' => true)); - $description = $markdown->output(); - } - else if (! empty($payload['TextBody'])) { - $description = $payload['TextBody']; - } - else { - $description = ''; - } - - // Finally, we create the task - return (bool) $this->taskCreation->create(array( - 'project_id' => $project['id'], - 'title' => $payload['Subject'], - 'description' => $description, - 'creator_id' => $user['id'], - )); - } -} diff --git a/app/Integration/Smtp.php b/app/Integration/Smtp.php new file mode 100644 index 00000000..ad2f30f8 --- /dev/null +++ b/app/Integration/Smtp.php @@ -0,0 +1,71 @@ +setSubject($subject) + ->setFrom(array(MAIL_FROM => $author)) + ->setBody($html, 'text/html') + ->setTo(array($email => $name)); + + Swift_Mailer::newInstance($this->getTransport())->send($message); + } + catch (Swift_TransportException $e) { + $this->container['logger']->error($e->getMessage()); + } + } + + /** + * Get SwiftMailer transport + * + * @access private + * @return \Swift_Transport + */ + private function getTransport() + { + switch (MAIL_TRANSPORT) { + case 'smtp': + $transport = Swift_SmtpTransport::newInstance(MAIL_SMTP_HOSTNAME, MAIL_SMTP_PORT); + $transport->setUsername(MAIL_SMTP_USERNAME); + $transport->setPassword(MAIL_SMTP_PASSWORD); + $transport->setEncryption(MAIL_SMTP_ENCRYPTION); + break; + case 'sendmail': + $transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND); + break; + default: + $transport = Swift_MailTransport::newInstance(); + } + + return $transport; + } +} diff --git a/app/Model/Notification.php b/app/Model/Notification.php index 048b6a39..0c2a5d24 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -4,9 +4,6 @@ namespace Model; use Core\Session; use Core\Translator; -use Swift_Message; -use Swift_Mailer; -use Swift_TransportException; /** * Notification model @@ -101,43 +98,22 @@ class Notification extends Base */ public function sendEmails($template, array $users, array $data) { - try { + foreach ($users as $user) { - $author = ''; - - if (Session::isOpen() && $this->userSession->isLogged()) { - $author = e('%s via Kanboard', $this->user->getFullname($this->session['user'])); + // Use the user language otherwise use the application language (do not use the session language) + if (! empty($user['language'])) { + Translator::load($user['language']); } - - $mailer = Swift_Mailer::newInstance($this->container['mailer']); - - foreach ($users as $user) { - - $this->container['logger']->debug('Send email notification to '.$user['username'].' lang='.$user['language']); - $start_time = microtime(true); - - // Use the user language otherwise use the application language (do not use the session language) - if (! empty($user['language'])) { - Translator::load($user['language']); - } - else { - Translator::load($this->config->get('application_language', 'en_US')); - } - - // Send the message - $message = Swift_Message::newInstance() - ->setSubject($this->getMailSubject($template, $data)) - ->setFrom(array(MAIL_FROM => $author ?: 'Kanboard')) - ->setBody($this->getMailContent($template, $data), 'text/html') - ->setTo(array($user['email'] => $user['name'] ?: $user['username'])); - - $mailer->send($message); - - $this->container['logger']->debug('Email sent in '.round(microtime(true) - $start_time, 6).' seconds'); + else { + Translator::load($this->config->get('application_language', 'en_US')); } - } - catch (Swift_TransportException $e) { - $this->container['logger']->error($e->getMessage()); + + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + $this->getMailSubject($template, $data), + $this->getMailContent($template, $data) + ); } // Restore locales @@ -167,40 +143,40 @@ class Notification extends Base { switch ($template) { case 'file_creation': - $subject = $this->getStandardMailSubject(t('New attachment'), $data); + $subject = $this->getStandardMailSubject(e('New attachment'), $data); break; case 'comment_creation': - $subject = $this->getStandardMailSubject(t('New comment'), $data); + $subject = $this->getStandardMailSubject(e('New comment'), $data); break; case 'comment_update': - $subject = $this->getStandardMailSubject(t('Comment updated'), $data); + $subject = $this->getStandardMailSubject(e('Comment updated'), $data); break; case 'subtask_creation': - $subject = $this->getStandardMailSubject(t('New subtask'), $data); + $subject = $this->getStandardMailSubject(e('New subtask'), $data); break; case 'subtask_update': - $subject = $this->getStandardMailSubject(t('Subtask updated'), $data); + $subject = $this->getStandardMailSubject(e('Subtask updated'), $data); break; case 'task_creation': - $subject = $this->getStandardMailSubject(t('New task'), $data); + $subject = $this->getStandardMailSubject(e('New task'), $data); break; case 'task_update': - $subject = $this->getStandardMailSubject(t('Task updated'), $data); + $subject = $this->getStandardMailSubject(e('Task updated'), $data); break; case 'task_close': - $subject = $this->getStandardMailSubject(t('Task closed'), $data); + $subject = $this->getStandardMailSubject(e('Task closed'), $data); break; case 'task_open': - $subject = $this->getStandardMailSubject(t('Task opened'), $data); + $subject = $this->getStandardMailSubject(e('Task opened'), $data); break; case 'task_move_column': - $subject = $this->getStandardMailSubject(t('Column Change'), $data); + $subject = $this->getStandardMailSubject(e('Column Change'), $data); break; case 'task_move_position': - $subject = $this->getStandardMailSubject(t('Position Change'), $data); + $subject = $this->getStandardMailSubject(e('Position Change'), $data); break; case 'task_assignee_change': - $subject = $this->getStandardMailSubject(t('Assignee Change'), $data); + $subject = $this->getStandardMailSubject(e('Assignee Change'), $data); break; case 'task_due': $subject = e('[%s][Due tasks]', $data['project']); diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index ced7c7c6..3fc6c850 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -64,6 +64,7 @@ class ClassProvider implements ServiceProviderInterface 'Webhook', ), 'Core' => array( + 'EmailClient', 'Helper', 'HttpClient', 'MemoryCache', @@ -78,9 +79,10 @@ class ClassProvider implements ServiceProviderInterface 'HipchatWebhook', 'Jabber', 'MailgunWebhook', - 'PostmarkWebhook', + 'Postmark', 'SendgridWebhook', 'SlackWebhook', + 'Smtp', ) ); diff --git a/app/ServiceProvider/MailerProvider.php b/app/ServiceProvider/MailerProvider.php deleted file mode 100644 index 6469a737..00000000 --- a/app/ServiceProvider/MailerProvider.php +++ /dev/null @@ -1,33 +0,0 @@ -setUsername(MAIL_SMTP_USERNAME); - $transport->setPassword(MAIL_SMTP_PASSWORD); - $transport->setEncryption(MAIL_SMTP_ENCRYPTION); - break; - case 'sendmail': - $transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND); - break; - default: - $transport = Swift_MailTransport::newInstance(); - } - - return $transport; - }; - } -} diff --git a/app/common.php b/app/common.php index 01c3077b..f4485267 100644 --- a/app/common.php +++ b/app/common.php @@ -27,4 +27,3 @@ $container->register(new ServiceProvider\LoggingProvider); $container->register(new ServiceProvider\DatabaseProvider); $container->register(new ServiceProvider\ClassProvider); $container->register(new ServiceProvider\EventDispatcherProvider); -$container->register(new ServiceProvider\MailerProvider); diff --git a/app/constants.php b/app/constants.php index df57fe30..b487e0bd 100644 --- a/app/constants.php +++ b/app/constants.php @@ -64,6 +64,7 @@ defined('MAIL_SMTP_USERNAME') or define('MAIL_SMTP_USERNAME', ''); defined('MAIL_SMTP_PASSWORD') or define('MAIL_SMTP_PASSWORD', ''); defined('MAIL_SMTP_ENCRYPTION') or define('MAIL_SMTP_ENCRYPTION', null); defined('MAIL_SENDMAIL_COMMAND') or define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'); +defined('POSTMARK_API_TOKEN') or define('POSTMARK_API_TOKEN', ''); // Enable or disable "Strict-Transport-Security" HTTP header defined('ENABLE_HSTS') or define('ENABLE_HSTS', true); diff --git a/config.default.php b/config.default.php index 7f379f5c..99698420 100644 --- a/config.default.php +++ b/config.default.php @@ -14,7 +14,7 @@ define('FILES_DIR', 'data/files/'); // E-mail address for the "From" header (notifications) define('MAIL_FROM', 'notifications@kanboard.local'); -// Mail transport to use: "smtp", "sendmail" or "mail" (PHP mail function) +// Mail transport to use: "smtp", "sendmail", "mail" (PHP mail function), "postmark" define('MAIL_TRANSPORT', 'mail'); // SMTP configuration to use when the "smtp" transport is chosen @@ -27,6 +27,9 @@ define('MAIL_SMTP_ENCRYPTION', null); // Valid values are "null", "ssl" or "tls" // Sendmail command to use when the transport is "sendmail" define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'); +// Postmark API token (used to send emails through their API) +define('POSTMARK_API_TOKEN', ''); + // Database driver: sqlite, mysql or postgres (sqlite by default) define('DB_DRIVER', 'sqlite'); diff --git a/docs/email-configuration.markdown b/docs/email-configuration.markdown index d5559a8f..33d921bb 100644 --- a/docs/email-configuration.markdown +++ b/docs/email-configuration.markdown @@ -12,6 +12,16 @@ To receive email notifications, users of Kanboard must have: Note: The logged user who performs the action doesn't receive any notifications, only other project members. +Email transports +---------------- + +There are several email transports available: + +- SMTP +- Sendmail +- PHP native mail function +- Postmark + Server settings --------------- @@ -57,6 +67,34 @@ define('MAIL_TRANSPORT', 'sendmail'); define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'); ``` +### PHP native mail function + +This is the default configuration: + +```php +define('MAIL_TRANSPORT', 'mail'); +``` + +### Postmark HTTP API + +Postmark is a third-party email service. +If you already use the Postmark integration to receive emails in Kanboard you can use the same provider to send email too. + +This system use their HTTP API instead of the SMTP protocol. + +Here are the required settings for this configuration: + +```php +// We choose "postmark" as mail transport +define('MAIL_TRANSPORT', 'postmark'); + +// Copy and paste your Postmark API token +define('POSTMARK_API_TOKEN', 'COPY HERE YOUR POSTMARK API TOKEN'); + +// Be sure to use the Postmark configured sender email address +define('MAIL_FROM', 'sender-address-configured-in-postmark@example.org'); +``` + ### The sender email address By default, emails will use the sender address `notifications@kanboard.local`. diff --git a/tests/units/Base.php b/tests/units/Base.php index 20f4a8cc..d5fea977 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -15,6 +15,7 @@ class FakeHttpClient { private $url = ''; private $data = array(); + private $headers = array(); public function getUrl() { @@ -26,16 +27,21 @@ class FakeHttpClient return $this->data; } + public function getHeaders() + { + return $this->headers; + } + public function toPrettyJson() { return json_encode($this->data, JSON_PRETTY_PRINT); } - public function post($url, array $data) + public function post($url, array $data, array $headers = array()) { $this->url = $url; $this->data = $data; - //echo $this->toPrettyJson(); + $this->headers = $headers; return true; } } diff --git a/tests/units/PostmarkTest.php b/tests/units/PostmarkTest.php new file mode 100644 index 00000000..b708217d --- /dev/null +++ b/tests/units/PostmarkTest.php @@ -0,0 +1,106 @@ +container); + $pm->sendEmail('test@localhost', 'Me', 'Test', 'Content', 'Bob'); + + $this->assertEquals('https://api.postmarkapp.com/email', $this->container['httpClient']->getUrl()); + + $data = $this->container['httpClient']->getData(); + + $this->assertArrayHasKey('From', $data); + $this->assertArrayHasKey('To', $data); + $this->assertArrayHasKey('Subject', $data); + $this->assertArrayHasKey('HtmlBody', $data); + + $this->assertEquals('Me ', $data['To']); + $this->assertEquals('Bob ', $data['From']); + $this->assertEquals('Test', $data['Subject']); + $this->assertEquals('Content', $data['HtmlBody']); + + $this->assertContains('Accept: application/json', $this->container['httpClient']->getHeaders()); + $this->assertContains('X-Postmark-Server-Token: ', $this->container['httpClient']->getHeaders()); + } + + public function testHandlePayload() + { + $w = new Postmark($this->container); + $p = new Project($this->container); + $pp = new ProjectPermission($this->container); + $u = new User($this->container); + $tc = new TaskCreation($this->container); + $tf = new TaskFinder($this->container); + + $this->assertEquals(2, $u->create(array('name' => 'me', 'email' => 'me@localhost'))); + + $this->assertEquals(1, $p->create(array('name' => 'test1'))); + $this->assertEquals(2, $p->create(array('name' => 'test2', 'identifier' => 'TEST1'))); + + // Empty payload + $this->assertFalse($w->receiveEmail(array())); + + // Unknown user + $this->assertFalse($w->receiveEmail(array('From' => 'a@b.c', 'Subject' => 'Email task', 'MailboxHash' => 'foobar', 'TextBody' => 'boo'))); + + // Project not found + $this->assertFalse($w->receiveEmail(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test', 'TextBody' => 'boo'))); + + // User is not member + $this->assertFalse($w->receiveEmail(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo'))); + $this->assertTrue($pp->addMember(2, 2)); + + // The task must be created + $this->assertTrue($w->receiveEmail(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo'))); + + $task = $tf->getById(1); + $this->assertNotEmpty($task); + $this->assertEquals(2, $task['project_id']); + $this->assertEquals('Email task', $task['title']); + $this->assertEquals('boo', $task['description']); + $this->assertEquals(2, $task['creator_id']); + } + + public function testHtml2Markdown() + { + $w = new Postmark($this->container); + $p = new Project($this->container); + $pp = new ProjectPermission($this->container); + $u = new User($this->container); + $tc = new TaskCreation($this->container); + $tf = new TaskFinder($this->container); + + $this->assertEquals(2, $u->create(array('name' => 'me', 'email' => 'me@localhost'))); + $this->assertEquals(1, $p->create(array('name' => 'test2', 'identifier' => 'TEST1'))); + $this->assertTrue($pp->addMember(1, 2)); + + $this->assertTrue($w->receiveEmail(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo', 'HtmlBody' => '

boo

'))); + + $task = $tf->getById(1); + $this->assertNotEmpty($task); + $this->assertEquals(1, $task['project_id']); + $this->assertEquals('Email task', $task['title']); + $this->assertEquals('**boo**', $task['description']); + $this->assertEquals(2, $task['creator_id']); + + $this->assertTrue($w->receiveEmail(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => '**boo**', 'HtmlBody' => ''))); + + $task = $tf->getById(2); + $this->assertNotEmpty($task); + $this->assertEquals(1, $task['project_id']); + $this->assertEquals('Email task', $task['title']); + $this->assertEquals('**boo**', $task['description']); + $this->assertEquals(2, $task['creator_id']); + } +} diff --git a/tests/units/PostmarkWebhookTest.php b/tests/units/PostmarkWebhookTest.php deleted file mode 100644 index 34be8515..00000000 --- a/tests/units/PostmarkWebhookTest.php +++ /dev/null @@ -1,83 +0,0 @@ -container); - $p = new Project($this->container); - $pp = new ProjectPermission($this->container); - $u = new User($this->container); - $tc = new TaskCreation($this->container); - $tf = new TaskFinder($this->container); - - $this->assertEquals(2, $u->create(array('name' => 'me', 'email' => 'me@localhost'))); - - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(2, $p->create(array('name' => 'test2', 'identifier' => 'TEST1'))); - - // Empty payload - $this->assertFalse($w->parsePayload(array())); - - // Unknown user - $this->assertFalse($w->parsePayload(array('From' => 'a@b.c', 'Subject' => 'Email task', 'MailboxHash' => 'foobar', 'TextBody' => 'boo'))); - - // Project not found - $this->assertFalse($w->parsePayload(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test', 'TextBody' => 'boo'))); - - // User is not member - $this->assertFalse($w->parsePayload(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo'))); - $this->assertTrue($pp->addMember(2, 2)); - - // The task must be created - $this->assertTrue($w->parsePayload(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo'))); - - $task = $tf->getById(1); - $this->assertNotEmpty($task); - $this->assertEquals(2, $task['project_id']); - $this->assertEquals('Email task', $task['title']); - $this->assertEquals('boo', $task['description']); - $this->assertEquals(2, $task['creator_id']); - } - - public function testHtml2Markdown() - { - $w = new PostmarkWebhook($this->container); - $p = new Project($this->container); - $pp = new ProjectPermission($this->container); - $u = new User($this->container); - $tc = new TaskCreation($this->container); - $tf = new TaskFinder($this->container); - - $this->assertEquals(2, $u->create(array('name' => 'me', 'email' => 'me@localhost'))); - $this->assertEquals(1, $p->create(array('name' => 'test2', 'identifier' => 'TEST1'))); - $this->assertTrue($pp->addMember(1, 2)); - - $this->assertTrue($w->parsePayload(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => 'boo', 'HtmlBody' => '

boo

'))); - - $task = $tf->getById(1); - $this->assertNotEmpty($task); - $this->assertEquals(1, $task['project_id']); - $this->assertEquals('Email task', $task['title']); - $this->assertEquals('**boo**', $task['description']); - $this->assertEquals(2, $task['creator_id']); - - $this->assertTrue($w->parsePayload(array('From' => 'me@localhost', 'Subject' => 'Email task', 'MailboxHash' => 'test1', 'TextBody' => '**boo**', 'HtmlBody' => ''))); - - $task = $tf->getById(2); - $this->assertNotEmpty($task); - $this->assertEquals(1, $task['project_id']); - $this->assertEquals('Email task', $task['title']); - $this->assertEquals('**boo**', $task['description']); - $this->assertEquals(2, $task['creator_id']); - } -} -- cgit v1.2.3