diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/Controller/Webhook.php | 2 | ||||
-rw-r--r-- | app/Core/Base.php | 4 | ||||
-rw-r--r-- | app/Core/EmailClient.php | 43 | ||||
-rw-r--r-- | app/Core/HttpClient.php | 51 | ||||
-rw-r--r-- | app/Integration/Postmark.php (renamed from app/Integration/PostmarkWebhook.php) | 39 | ||||
-rw-r--r-- | app/Integration/Smtp.php | 71 | ||||
-rw-r--r-- | app/Model/Notification.php | 74 | ||||
-rw-r--r-- | app/ServiceProvider/ClassProvider.php | 4 | ||||
-rw-r--r-- | app/ServiceProvider/MailerProvider.php | 33 | ||||
-rw-r--r-- | app/common.php | 1 | ||||
-rw-r--r-- | app/constants.php | 1 |
11 files changed, 200 insertions, 123 deletions
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 @@ +<?php + +namespace Core; + +/** + * Mail client + * + * @package core + * @author Frederic Guillot + */ +class EmailClient extends Base +{ + /** + * Send a HTML email + * + * @access public + * @param string $email + * @param string $name + * @param string $subject + * @param string $html + */ + public function send($email, $name, $subject, $html) + { + $this->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/PostmarkWebhook.php b/app/Integration/Postmark.php index 9051e5f7..a367c23e 100644 --- a/app/Integration/PostmarkWebhook.php +++ b/app/Integration/Postmark.php @@ -5,21 +5,48 @@ namespace Integration; use HTML_To_Markdown; /** - * Postmark Webhook + * Postmark integration * * @package integration * @author Frederic Guillot */ -class PostmarkWebhook extends \Core\Base +class Postmark extends \Core\Base { /** + * Send a HTML email + * + * @access public + * @param string $email + * @param string $name + * @param string $subject + * @param string $html + * @param string $author + */ + public function sendEmail($email, $name, $subject, $html, $author) + { + $headers = array( + 'Accept: application/json', + 'X-Postmark-Server-Token: '.POSTMARK_API_TOKEN, + ); + + $payload = array( + 'From' => 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 parsePayload(array $payload) + public function receiveEmail(array $payload) { if (empty($payload['From']) || empty($payload['Subject']) || empty($payload['MailboxHash'])) { return false; @@ -29,7 +56,7 @@ class PostmarkWebhook extends \Core\Base $user = $this->user->getByEmail($payload['From']); if (empty($user)) { - $this->container['logger']->debug('PostmarkWebhook: ignored => user not found'); + $this->container['logger']->debug('Postmark: ignored => user not found'); return false; } @@ -37,13 +64,13 @@ class PostmarkWebhook extends \Core\Base $project = $this->project->getByIdentifier($payload['MailboxHash']); if (empty($project)) { - $this->container['logger']->debug('PostmarkWebhook: ignored => project not found'); + $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('PostmarkWebhook: ignored => user is not member of the project'); + $this->container['logger']->debug('Postmark: ignored => user is not member of the project'); return false; } 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 @@ +<?php + +namespace Integration; + +use Swift_Message; +use Swift_Mailer; +use Swift_MailTransport; +use Swift_SendmailTransport; +use Swift_SmtpTransport; +use Swift_TransportException; + +/** + * Smtp + * + * @package integration + * @author Frederic Guillot + */ +class Smtp extends \Core\Base +{ + /** + * Send a HTML email + * + * @access public + * @param string $email + * @param string $name + * @param string $subject + * @param string $html + * @param string $author + */ + public function sendEmail($email, $name, $subject, $html, $author) + { + try { + + $message = Swift_Message::newInstance() + ->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 @@ -<?php - -namespace ServiceProvider; - -use Pimple\Container; -use Pimple\ServiceProviderInterface; -use Swift_SmtpTransport; -use Swift_SendmailTransport; -use Swift_MailTransport; - -class MailerProvider implements ServiceProviderInterface -{ - public function register(Container $container) - { - $container['mailer'] = function () { - 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/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); |