Make mail transports pluggable and move integrations to plugins

- Postmark: https://github.com/kanboard/plugin-postmark
- Mailgun: https://github.com/kanboard/plugin-mailgun
- Sendgrid: https://github.com/kanboard/plugin-sendgrid
This commit is contained in:
Frederic Guillot
2015-10-16 20:50:12 -04:00
parent 9c9ed02cd7
commit f99a3c501f
55 changed files with 652 additions and 1457 deletions

View File

@@ -92,37 +92,4 @@ class Webhook extends Base
echo $result ? 'PARSED' : 'IGNORED';
}
/**
* Handle Postmark webhooks
*
* @access public
*/
public function postmark()
{
$this->checkWebhookToken();
echo $this->postmark->receiveEmail($this->request->getJson()) ? 'PARSED' : 'IGNORED';
}
/**
* Handle Mailgun webhooks
*
* @access public
*/
public function mailgun()
{
$this->checkWebhookToken();
echo $this->mailgun->receiveEmail($_POST) ? 'PARSED' : 'IGNORED';
}
/**
* Handle Sendgrid webhooks
*
* @access public
*/
public function sendgrid()
{
$this->checkWebhookToken();
echo $this->sendgrid->receiveEmail($_POST) ? 'PARSED' : 'IGNORED';
}
}

View File

@@ -11,7 +11,7 @@ use Pimple\Container;
* @author Frederic Guillot
*
* @property \Kanboard\Core\Helper $helper
* @property \Kanboard\Core\EmailClient $emailClient
* @property \Kanboard\Core\Mail\Client $emailClient
* @property \Kanboard\Core\HttpClient $httpClient
* @property \Kanboard\Core\Paginator $paginator
* @property \Kanboard\Core\Request $request
@@ -29,11 +29,7 @@ use Pimple\Container;
* @property \Kanboard\Integration\GitlabWebhook $gitlabWebhook
* @property \Kanboard\Integration\HipchatWebhook $hipchatWebhook
* @property \Kanboard\Integration\Jabber $jabber
* @property \Kanboard\Integration\Mailgun $mailgun
* @property \Kanboard\Integration\Postmark $postmark
* @property \Kanboard\Integration\Sendgrid $sendgrid
* @property \Kanboard\Integration\SlackWebhook $slackWebhook
* @property \Kanboard\Integration\Smtp $smtp
* @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter
* @property \Kanboard\Formatter\TaskFilterGanttFormatter $taskFilterGanttFormatter
* @property \Kanboard\Formatter\TaskFilterAutoCompleteFormatter $taskFilterAutoCompleteFormatter

View File

@@ -1,49 +0,0 @@
<?php
namespace Kanboard\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 'sendgrid':
$this->sendgrid->sendEmail($email, $name, $subject, $html, $author);
break;
case 'mailgun':
$this->mailgun->sendEmail($email, $name, $subject, $html, $author);
break;
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');
}
}

96
app/Core/Mail/Client.php Normal file
View File

@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Core\Mail;
use Pimple\Container;
use Kanboard\Core\Base;
/**
* Mail Client
*
* @package mail
* @author Frederic Guillot
*/
class Client extends Base
{
/**
* Mail transport instances
*
* @access private
* @var \Pimple\Container
*/
private $transports;
/**
* Constructor
*
* @access public
* @param \Pimple\Container $container
*/
public function __construct(Container $container)
{
parent::__construct($container);
$this->transports = new Container;
}
/**
* Send a HTML email
*
* @access public
* @param string $email
* @param string $name
* @param string $subject
* @param string $html
* @return EmailClient
*/
public function send($email, $name, $subject, $html)
{
$this->container['logger']->debug('Sending email to '.$email.' ('.MAIL_TRANSPORT.')');
$start_time = microtime(true);
$author = 'Kanboard';
if ($this->userSession->isLogged()) {
$author = e('%s via Kanboard', $this->user->getFullname($this->session['user']));
}
$this->getTransport(MAIL_TRANSPORT)->sendEmail($email, $name, $subject, $html, $author);
if (DEBUG) {
$this->logger->debug('Email sent in '.round(microtime(true) - $start_time, 6).' seconds');
}
return $this;
}
/**
* Get mail transport instance
*
* @access public
* @param string $transport
* @return EmailClientInterface
*/
public function getTransport($transport)
{
return $this->transports[$transport];
}
/**
* Add a new mail transport
*
* @access public
* @param string $transport
* @param string $class
* @return EmailClient
*/
public function setTransport($transport, $class)
{
$container = $this->container;
$this->transports[$transport] = function() use ($class, $container) {
return new $class($container);
};
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Kanboard\Core\Mail;
/**
* Mail Client Interface
*
* @package mail
* @author Frederic Guillot
*/
interface ClientInterface
{
/**
* 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);
}

View File

@@ -1,21 +1,21 @@
<?php
namespace Kanboard\Integration;
namespace Kanboard\Core\Mail\Transport;
use Swift_Message;
use Swift_Mailer;
use Swift_MailTransport;
use Swift_SendmailTransport;
use Swift_SmtpTransport;
use Swift_TransportException;
use Kanboard\Core\Base;
use Kanboard\Core\Mail\ClientInterface;
/**
* Smtp
* PHP Mail Handler
*
* @package integration
* @package transport
* @author Frederic Guillot
*/
class Smtp extends \Kanboard\Core\Base
class Mail extends Base implements ClientInterface
{
/**
* Send a HTML email
@@ -40,32 +40,18 @@ class Smtp extends \Kanboard\Core\Base
Swift_Mailer::newInstance($this->getTransport())->send($message);
}
catch (Swift_TransportException $e) {
$this->container['logger']->error($e->getMessage());
$this->logger->error($e->getMessage());
}
}
/**
* Get SwiftMailer transport
*
* @access private
* @access protected
* @return \Swift_Transport
*/
private function getTransport()
protected 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;
return Swift_MailTransport::newInstance();
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Kanboard\Core\Mail\Transport;
use Swift_SendmailTransport;
/**
* PHP Mail Handler
*
* @package transport
* @author Frederic Guillot
*/
class Sendmail extends Mail
{
/**
* Get SwiftMailer transport
*
* @access protected
* @return \Swift_Transport
*/
protected function getTransport()
{
return Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Kanboard\Core\Mail\Transport;
use Swift_SmtpTransport;
/**
* PHP Mail Handler
*
* @package transport
* @author Frederic Guillot
*/
class Smtp extends Mail
{
/**
* Get SwiftMailer transport
*
* @access protected
* @return \Swift_Transport
*/
protected function getTransport()
{
$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);
return $transport;
}
}

View File

@@ -66,7 +66,7 @@ abstract class Base extends \Kanboard\Core\Base
*/
public function getPluginName()
{
return ucfirst(substr(get_called_class(), 7, -7));
return ucfirst(substr(get_called_class(), 16, -7));
}
/**

View File

@@ -1,95 +0,0 @@
<?php
namespace Kanboard\Integration;
use Kanboard\Core\Tool;
/**
* Mailgun Integration
*
* @package integration
* @author Frederic Guillot
*/
class Mailgun extends \Kanboard\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(
'Authorization: Basic '.base64_encode('api:'.MAILGUN_API_TOKEN)
);
$payload = array(
'from' => sprintf('%s <%s>', $author, MAIL_FROM),
'to' => sprintf('%s <%s>', $name, $email),
'subject' => $subject,
'html' => $html,
);
$this->httpClient->postForm('https://api.mailgun.net/v3/'.MAILGUN_DOMAIN.'/messages', $payload, $headers);
}
/**
* Parse incoming email
*
* @access public
* @param array $payload Incoming email
* @return boolean
*/
public function receiveEmail(array $payload)
{
if (empty($payload['sender']) || empty($payload['subject']) || empty($payload['recipient'])) {
return false;
}
// The user must exists in Kanboard
$user = $this->user->getByEmail($payload['sender']);
if (empty($user)) {
$this->container['logger']->debug('Mailgun: ignored => user not found');
return false;
}
// The project must have a short name
$project = $this->project->getByIdentifier(Tool::getMailboxHash($payload['recipient']));
if (empty($project)) {
$this->container['logger']->debug('Mailgun: 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('Mailgun: ignored => user is not member of the project');
return false;
}
// Get the Markdown contents
if (! empty($payload['stripped-html'])) {
$description = $this->htmlConverter->convert($payload['stripped-html']);
}
else if (! empty($payload['stripped-text'])) {
$description = $payload['stripped-text'];
}
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'],
));
}
}

View File

@@ -1,94 +0,0 @@
<?php
namespace Kanboard\Integration;
/**
* Postmark integration
*
* @package integration
* @author Frederic Guillot
*/
class Postmark extends \Kanboard\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->postJson('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'])) {
$description = $this->htmlConverter->convert($payload['HtmlBody']);
}
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'],
));
}
}

View File

@@ -1,98 +0,0 @@
<?php
namespace Kanboard\Integration;
use Kanboard\Core\Tool;
/**
* Sendgrid Integration
*
* @package integration
* @author Frederic Guillot
*/
class Sendgrid extends \Kanboard\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)
{
$payload = array(
'api_user' => SENDGRID_API_USER,
'api_key' => SENDGRID_API_KEY,
'to' => $email,
'toname' => $name,
'from' => MAIL_FROM,
'fromname' => $author,
'html' => $html,
'subject' => $subject,
);
$this->httpClient->postForm('https://api.sendgrid.com/api/mail.send.json', $payload);
}
/**
* Parse incoming email
*
* @access public
* @param array $payload Incoming email
* @return boolean
*/
public function receiveEmail(array $payload)
{
if (empty($payload['envelope']) || empty($payload['subject'])) {
return false;
}
$envelope = json_decode($payload['envelope'], true);
$sender = isset($envelope['to'][0]) ? $envelope['to'][0] : '';
// The user must exists in Kanboard
$user = $this->user->getByEmail($envelope['from']);
if (empty($user)) {
$this->container['logger']->debug('SendgridWebhook: ignored => user not found');
return false;
}
// The project must have a short name
$project = $this->project->getByIdentifier(Tool::getMailboxHash($sender));
if (empty($project)) {
$this->container['logger']->debug('SendgridWebhook: 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('SendgridWebhook: ignored => user is not member of the project');
return false;
}
// Get the Markdown contents
if (! empty($payload['html'])) {
$description = $this->htmlConverter->convert($payload['html']);
}
else if (! empty($payload['text'])) {
$description = $payload['text'];
}
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'],
));
}
}

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kopie',
'Project cloned successfully.' => 'Kopie projektu byla úspěšně vytvořena.',
'Unable to clone this project.' => 'Kopii projektu nelze vytvořit.',
'Email notifications' => 'Upozornění E-Mailem ',
'Enable email notifications' => 'Povolit upozornění pomocí e-mailů',
'Task position:' => 'Pořadí úkolu:',
'The task #%d have been opened.' => 'Úkol #%d byl znovu otevřen.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Schwedische Kronen',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Identifikátor projektu je volitelný alfanumerický kód používaný k identifikaci vašeho projektu.',
'Identifier' => 'Identifikator',
'Postmark (incoming emails)' => 'Postmark (Eingehende E-Mails)',
'Help on Postmark integration' => 'Hilfe bei Postmark-Integration',
'Mailgun (incoming emails)' => 'Mailgun (Eingehende E-Mails)',
'Help on Mailgun integration' => 'Hilfe bei Mailgun-Integration',
'Sendgrid (incoming emails)' => 'Sendgrid (Eingehende E-Mails)',
'Help on Sendgrid integration' => 'Hilfe bei Sendgrid-Integration',
'Disable two factor authentication' => 'Zrušit dvou stupňovou autorizaci',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Nutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kopier',
'Project cloned successfully.' => 'Projektet er kopieret.',
'Unable to clone this project.' => 'Projektet kunne ikke kopieres',
'Email notifications' => 'Email notifikationer',
'Enable email notifications' => 'Aktivér email notifikationer',
'Task position:' => 'Opgave position:',
'The task #%d have been opened.' => 'Opgaven #%d er blevet åbnet.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'duplizieren',
'Project cloned successfully.' => 'Projekt wurde dupliziert.',
'Unable to clone this project.' => 'Duplizieren dieses Projekts schlug fehl.',
'Email notifications' => 'E-Mail-Benachrichtigungen',
'Enable email notifications' => 'E-Mail-Benachrichtigungen einschalten',
'Task position:' => 'Position der Aufgabe',
'The task #%d have been opened.' => 'Die Aufgabe #%d wurde geöffnet.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Schwedische Kronen',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Der Projektidentifikator ist ein optionaler alphanumerischer Code, der das Projekt identifiziert.',
'Identifier' => 'Identifikator',
'Postmark (incoming emails)' => 'Postmark (Eingehende E-Mails)',
'Help on Postmark integration' => 'Hilfe bei Postmark-Integration',
'Mailgun (incoming emails)' => 'Mailgun (Eingehende E-Mails)',
'Help on Mailgun integration' => 'Hilfe bei Mailgun-Integration',
'Sendgrid (incoming emails)' => 'Sendgrid (Eingehende E-Mails)',
'Help on Sendgrid integration' => 'Hilfe bei Sendgrid-Integration',
'Disable two factor authentication' => 'Deaktiviere Zwei-Faktor-Authentifizierung',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Nutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?',
'Edit link' => 'Verbindung bearbeiten',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Clonar',
'Project cloned successfully.' => 'Proyecto clonado correctamente',
'Unable to clone this project.' => 'Impsible clonar proyecto',
'Email notifications' => 'Notificaciones correo electrónico',
'Enable email notifications' => 'Habilitar notificaciones por correo electrónico',
'Task position:' => 'Posición de la tarea',
'The task #%d have been opened.' => 'La tarea #%d ha sido abierta',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Corona sueca',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'El identificador del proyecto us un código opcional alfanumérico que se usa para identificar su proyecto.',
'Identifier' => 'Identificador',
'Postmark (incoming emails)' => 'Matasellos (emails entrantes)',
'Help on Postmark integration' => 'Ayuda sobre la integración de Matasellos',
'Mailgun (incoming emails)' => 'Mailgun (emails entrantes)',
'Help on Mailgun integration' => 'Ayuda sobre la integración con Mailgun',
'Sendgrid (incoming emails)' => 'Sendgrid (emails entrantes)',
'Help on Sendgrid integration' => 'Ayuda sobre la integración con Sendgrid',
'Disable two factor authentication' => 'Desactivar la autenticación de dos factores',
'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmentes quiere desactuvar la autenticación de dos factores para este usuario: "%s?"',
'Edit link' => 'Editar enlace',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kahdenna',
'Project cloned successfully.' => 'Projekti kahdennettu onnistuneesti',
'Unable to clone this project.' => 'Projektin kahdennus epäonnistui',
'Email notifications' => 'Sähköposti-ilmoitukset',
'Enable email notifications' => 'Ota käyttöön sähköposti-ilmoitukset',
'Task position:' => 'Tehtävän sijainti',
'The task #%d have been opened.' => 'Tehtävä #%d on avattu',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Clone',
'Project cloned successfully.' => 'Projet cloné avec succès.',
'Unable to clone this project.' => 'Impossible de cloner ce projet.',
'Email notifications' => 'Notifications par email',
'Enable email notifications' => 'Activer les notifications par emails',
'Task position:' => 'Position de la tâche :',
'The task #%d have been opened.' => 'La tâche #%d a été ouverte.',
@@ -737,12 +736,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Couronne suédoise',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'L\'identificateur du projet est un code alpha-numérique optionnel pour identifier votre projet.',
'Identifier' => 'Identificateur',
'Postmark (incoming emails)' => 'Postmark (emails entrants)',
'Help on Postmark integration' => 'Aide sur l\'intégration avec Postmark',
'Mailgun (incoming emails)' => 'Mailgun (emails entrants)',
'Help on Mailgun integration' => 'Aide sur l\'intégration avec Mailgun',
'Sendgrid (incoming emails)' => 'Sendgrid (emails entrants)',
'Help on Sendgrid integration' => 'Aide sur l\'intégration avec Sendgrid',
'Disable two factor authentication' => 'Désactiver l\'authentification à deux facteurs',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Voulez-vous vraiment désactiver l\'authentification à deux facteurs pour cet utilisateur : « %s » ?',
'Edit link' => 'Modifier un lien',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Másolat',
'Project cloned successfully.' => 'A projekt sikeresen másolva.',
'Unable to clone this project.' => 'A projekt másolása sikertelen.',
'Email notifications' => 'E-mail értesítések',
'Enable email notifications' => 'E-mail értesítések engedélyezése',
'Task position:' => 'Feladat helye:',
'The task #%d have been opened.' => 'Feladat #%d megnyitva.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Klon',
'Project cloned successfully.' => 'Kloning proyek berhasil.',
'Unable to clone this project.' => 'Tidak dapat mengkloning proyek.',
'Email notifications' => 'Pemberitahuan email',
'Enable email notifications' => 'Aktifkan pemberitahuan dari email',
'Task position:' => 'Posisi tugas :',
'The task #%d have been opened.' => 'Tugas #%d telah dibuka.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Krona Swedia',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Identifier proyek adalah kode alfanumerik opsional digunakan untuk mengidentifikasi proyek Anda.',
'Identifier' => 'Identifier',
'Postmark (incoming emails)' => 'Postmark (email masuk)',
'Help on Postmark integration' => 'Bantuan pada integrasi Postmark',
'Mailgun (incoming emails)' => 'Mailgun (email masuk)',
'Help on Mailgun integration' => 'Bantuan pada integrasi Mailgun',
'Sendgrid (incoming emails)' => 'Sendgrid (email masuk)',
'Help on Sendgrid integration' => 'Bantuan pada integrasi Sendgrid',
'Disable two factor authentication' => 'Matikan dua faktor otentifikasi',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Apakah anda yakin akan mematikan dua faktor otentifikasi untuk pengguna ini : « %s » ?',
'Edit link' => 'Modifikasi tautan',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Clona',
'Project cloned successfully.' => 'Progetto clonato con successo.',
'Unable to clone this project.' => 'Impossibile clonare questo progetto',
'Email notifications' => 'Notifiche email',
'Enable email notifications' => 'Abilita le notifiche via email',
'Task position:' => 'Posizione del compito:',
'The task #%d have been opened.' => 'Il compito #%d è stato aperto.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => '複製',
'Project cloned successfully.' => 'プロジェクトを複製しました。',
'Unable to clone this project.' => 'プロジェクトの複製に失敗しました。',
'Email notifications' => 'メール通知',
'Enable email notifications' => 'メール通知を設定',
'Task position:' => 'タスクの位置:',
'The task #%d have been opened.' => 'タスク #%d をオープンしました。',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kopier',
'Project cloned successfully.' => 'Prosjektet er kopiert.',
'Unable to clone this project.' => 'Prosjektet kunne ikke kopieres',
'Email notifications' => 'Epostvarslinger',
'Enable email notifications' => 'Aktiver epostvarslinger',
'Task position:' => 'Oppgaveposisjon:',
'The task #%d have been opened.' => 'Oppgaven #%d er åpnet.',
@@ -730,17 +729,11 @@ return array(
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
'Add a screenshot' => 'Legg til et skjermbilde',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ta et skjermbilde og trykk CTRL+V for å lime det inn her.',
// 'Take a screenshot and press CTRL+V or +V to paste here.' => '',
'Screenshot uploaded successfully.' => 'Skjermbilde opplastet',
// 'SEK - Swedish Krona' => '',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Prosjektkoden er en alfanumerisk kode som kan brukes for å identifisere prosjektet',
'Identifier' => 'Prosjektkode',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kloon',
'Project cloned successfully.' => 'Project succesvol gekloond.',
'Unable to clone this project.' => 'Klonen van project niet gelukt.',
'Email notifications' => 'Email notificatie',
'Enable email notifications' => 'Email notificatie aanzetten',
'Task position:' => 'Taak positie :',
'The task #%d have been opened.' => 'Taak #%d is geopend.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Sklonuj',
'Project cloned successfully.' => 'Projekt sklonowany pomyślnie.',
'Unable to clone this project.' => 'Nie można sklonować projektu.',
'Email notifications' => 'Powiadomienia email',
'Enable email notifications' => 'Włącz powiadomienia email',
'Task position:' => 'Pozycja zadania:',
'The task #%d have been opened.' => 'Zadania #%d zostały otwarte.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Korona szwedzka',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Identyfikator projektu to opcjonalny kod alfanumeryczny do identyfikacji projektu.',
'Identifier' => 'Identyfikator',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
'Disable two factor authentication' => 'Wyłącz uwierzytelnianie dwuetapowe',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Czy na pewno chcesz wyłączyć uwierzytelnianie dwuetapowe dla tego użytkownika: "%s"?',
'Edit link' => 'Edytuj link',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Clonar',
'Project cloned successfully.' => 'Projeto clonado com sucesso.',
'Unable to clone this project.' => 'Não foi possível clonar este projeto.',
'Email notifications' => 'Notificações por email',
'Enable email notifications' => 'Habilitar notificações por email',
'Task position:' => 'Posição da tarefa:',
'The task #%d have been opened.' => 'A tarefa #%d foi aberta.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Coroa sueca',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'O identificador de projeto é um código alfanumérico opcional utilizado para identificar o seu projeto.',
'Identifier' => 'Identificador',
'Postmark (incoming emails)' => 'Postmark (e-mails recebidos)',
'Help on Postmark integration' => 'Ajuda na integração do Postmark',
'Mailgun (incoming emails)' => 'Mailgun (e-mails recebidos)',
'Help on Mailgun integration' => 'Ajuda na integração do Mailgun',
'Sendgrid (incoming emails)' => 'Sendgrid (e-mails recebidos)',
'Help on Sendgrid integration' => 'Ajuda na integração do Sendgrid',
'Disable two factor authentication' => 'Desativar autenticação à dois fatores',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Você deseja realmente desativar a autenticação à dois fatores para esse usuário: "%s"?',
'Edit link' => 'Editar um link',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Clonar',
'Project cloned successfully.' => 'Projecto clonado com sucesso.',
'Unable to clone this project.' => 'Não foi possível clonar este projecto.',
'Email notifications' => 'Notificações por email',
'Enable email notifications' => 'Activar notificações por email',
'Task position:' => 'Posição da tarefa:',
'The task #%d have been opened.' => 'A tarefa #%d foi aberta.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Coroa sueca',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'O identificador de projecto é um código alfanumérico opcional utilizado para identificar o seu projecto.',
'Identifier' => 'Identificador',
'Postmark (incoming emails)' => 'Postmark (e-mails recebidos)',
'Help on Postmark integration' => 'Ajuda na integração do Postmark',
'Mailgun (incoming emails)' => 'Mailgun (e-mails recebidos)',
'Help on Mailgun integration' => 'Ajuda na integração do Mailgun',
'Sendgrid (incoming emails)' => 'Sendgrid (e-mails recebidos)',
'Help on Sendgrid integration' => 'Ajuda na integração do Sendgrid',
'Disable two factor authentication' => 'Desactivar autenticação com dois factores',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Tem a certeza que quer desactivar a autenticação com dois factores para esse utilizador: "%s"?',
'Edit link' => 'Editar um link',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Клонировать',
'Project cloned successfully.' => 'Проект клонирован.',
'Unable to clone this project.' => 'Не удалось клонировать проект.',
'Email notifications' => 'Уведомления по e-mail',
'Enable email notifications' => 'Включить уведомления по e-mail',
'Task position:' => 'Позиция задачи:',
'The task #%d have been opened.' => 'Задача #%d была открыта.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Шведская крона',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Идентификатор проекта - это опциональный буквенно-цифровой код использующийся для идентификации проекта',
'Identifier' => 'Идентификатор',
'Postmark (incoming emails)' => 'Postmark (входящие сообщения)',
'Help on Postmark integration' => 'Справка о Postmark интеграции',
'Mailgun (incoming emails)' => 'Mailgun (входящие сообщения)',
'Help on Mailgun integration' => 'Справка о Mailgun интеграции',
'Sendgrid (incoming emails)' => 'Sendgrid (входящие сообщения)',
'Help on Sendgrid integration' => 'Справка о Sendgrid интеграции',
'Disable two factor authentication' => 'Выключить двухфакторную авторизацию',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Вы действительно хотите выключить двухфакторную авторизацию для пользователя "%s"?',
'Edit link' => 'Редактировать ссылку',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Iskopiraj',
'Project cloned successfully.' => 'Projekat uspešno iskopiran.',
'Unable to clone this project.' => 'Nije moguće iskopirati projekat.',
'Email notifications' => 'Obaveštenje e-mailom',
'Enable email notifications' => 'Omogući obaveštenja e-mailom',
'Task position:' => 'Pozicija zadatka:',
'The task #%d have been opened.' => 'Zadatak #%d je otvoren.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Klona',
'Project cloned successfully.' => 'Projektet har klonats.',
'Unable to clone this project.' => 'Kunde inte klona projektet.',
'Email notifications' => 'Epostnotiser',
'Enable email notifications' => 'Aktivera epostnotiser',
'Task position:' => 'Uppgiftsposition:',
'The task #%d have been opened.' => 'Uppgiften #%d har öppnats.',
@@ -735,12 +734,6 @@ return array(
'SEK - Swedish Krona' => 'SEK - Svensk Krona',
'The project identifier is an optional alphanumeric code used to identify your project.' => 'Projektidentifieraren är en valbar alfanumerisk kod som används för att identifiera ditt projekt.',
'Identifier' => 'Identifierare',
'Postmark (incoming emails)' => 'Postmark (inkommande e-post)',
'Help on Postmark integration' => 'Hjälp för Postmark integration',
'Mailgun (incoming emails)' => 'Mailgrun (inkommande e-post)',
'Help on Mailgun integration' => 'Hjälp för Mailgrun integration',
'Sendgrid (incoming emails)' => 'Sendgrid (inkommande e-post)',
'Help on Sendgrid integration' => 'Hjälp för Sendgrid integration',
'Disable two factor authentication' => 'Inaktivera två-faktors autentisering',
'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vill du verkligen inaktivera två-faktors autentisering för denna användare: "%s"?',
'Edit link' => 'Ändra länk',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'เลียนแบบ',
'Project cloned successfully.' => 'เลียนแบบโปรเจคเรียบร้อยแล้ว',
'Unable to clone this project.' => 'ไม่สามารถเลียบแบบโปรเจคได้',
'Email notifications' => 'อีเมลแจ้งเตือน',
'Enable email notifications' => 'เปิดอีเมลแจ้งเตือน',
'Task position:' => 'ตำแหน่งงาน',
'The task #%d have been opened.' => 'งานที่ #%d ถุกเปิด',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
'Edit link' => 'แก้ไขลิงค์',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => 'Kopya oluştur',
'Project cloned successfully.' => 'Proje kopyası başarıyla oluşturuldu.',
'Unable to clone this project.' => 'Proje kopyası oluşturulamadı.',
'Email notifications' => 'E-Posta bilgilendirmesi',
'Enable email notifications' => 'E-Posta bilgilendirmesini aç',
'Task position:' => 'Görev pozisyonu',
'The task #%d have been opened.' => '#%d numaralı görev açıldı.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
// 'Edit link' => '',

View File

@@ -353,7 +353,6 @@ return array(
'Clone' => '克隆',
'Project cloned successfully.' => '成功复制项目。',
'Unable to clone this project.' => '无法复制此项目',
'Email notifications' => '邮件通知',
'Enable email notifications' => '启用邮件通知',
'Task position:' => '任务位置:',
'The task #%d have been opened.' => '任务#%d已经被开启.',
@@ -735,12 +734,6 @@ return array(
// 'SEK - Swedish Krona' => '',
// 'The project identifier is an optional alphanumeric code used to identify your project.' => '',
// 'Identifier' => '',
// 'Postmark (incoming emails)' => '',
// 'Help on Postmark integration' => '',
// 'Mailgun (incoming emails)' => '',
// 'Help on Mailgun integration' => '',
// 'Sendgrid (incoming emails)' => '',
// 'Help on Sendgrid integration' => '',
// 'Disable two factor authentication' => '',
// 'Do you really want to disable the two factor authentication for this user: "%s"?' => '',
'Edit link' => '编辑链接',

View File

@@ -3,6 +3,7 @@
namespace Kanboard\ServiceProvider;
use Kanboard\Core\Plugin\Loader;
use Kanboard\Core\Mail\Client as EmailClient;
use Kanboard\Core\ObjectStorage\FileStorage;
use Kanboard\Core\Paginator;
use Kanboard\Core\OAuth2;
@@ -78,7 +79,6 @@ class ClassProvider implements ServiceProviderInterface
'ProjectGanttFormatter',
),
'Core' => array(
'EmailClient',
'Helper',
'HttpClient',
'Lexer',
@@ -99,11 +99,7 @@ class ClassProvider implements ServiceProviderInterface
'GitlabWebhook',
'HipchatWebhook',
'Jabber',
'Mailgun',
'Postmark',
'Sendgrid',
'SlackWebhook',
'Smtp',
)
);
@@ -127,6 +123,14 @@ class ClassProvider implements ServiceProviderInterface
return new FileStorage(FILES_DIR);
};
$container['emailClient'] = function($container) {
$mailer = new EmailClient($container);
$mailer->setTransport('smtp', '\Kanboard\Core\Mail\Transport\Smtp');
$mailer->setTransport('sendmail', '\Kanboard\Core\Mail\Transport\Sendmail');
$mailer->setTransport('mail', '\Kanboard\Core\Mail\Transport\Mail');
return $mailer;
};
$container['pluginLoader'] = new Loader($container);
$container['cspRules'] = array('style-src' => "'self' 'unsafe-inline'", 'img-src' => '* data:');

View File

@@ -6,6 +6,8 @@
<?= $this->form->csrf() ?>
<?= $this->hook->render('template:config:integrations', array('values' => $values)) ?>
<h3><i class="fa fa-google"></i> <?= t('Google Authentication') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('oauth', 'google', array(), false, '', true) ?>"/><br/>
@@ -24,24 +26,6 @@
<p class="form-help"><?= $this->url->doc(t('Help on Gitlab authentication'), 'gitlab-authentication') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/mailgun-icon.png"/>&nbsp;<?= t('Mailgun (incoming emails)') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'mailgun', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/>
<p class="form-help"><?= $this->url->doc(t('Help on Mailgun integration'), 'mailgun') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/sendgrid-icon.png"/>&nbsp;<?= t('Sendgrid (incoming emails)') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'sendgrid', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/>
<p class="form-help"><?= $this->url->doc(t('Help on Sendgrid integration'), 'sendgrid') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/postmark-icon.png"/>&nbsp;<?= t('Postmark (incoming emails)') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'postmark', array('token' => $values['webhook_token']), false, '', true) ?>"/><br/>
<p class="form-help"><?= $this->url->doc(t('Help on Postmark integration'), 'postmark') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/>&nbsp;<?= t('Gravatar') ?></h3>
<div class="listing">
<?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?>

View File

@@ -82,11 +82,6 @@ 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', '');
defined('MAILGUN_API_TOKEN') or define('MAILGUN_API_TOKEN', '');
defined('MAILGUN_DOMAIN') or define('MAILGUN_DOMAIN', '');
defined('SENDGRID_API_USER') or define('SENDGRID_API_USER', '');
defined('SENDGRID_API_KEY') or define('SENDGRID_API_KEY', '');
// Enable or disable "Strict-Transport-Security" HTTP header
defined('ENABLE_HSTS') or define('ENABLE_HSTS', true);