diff --git a/css/backoffice/pages/_all.scss b/css/backoffice/pages/_all.scss index d911ff390..3e6b6b39a 100644 --- a/css/backoffice/pages/_all.scss +++ b/css/backoffice/pages/_all.scss @@ -14,4 +14,5 @@ @import "csv-import"; @import "global-search"; @import "run-query"; -@import "welcome-popup"; \ No newline at end of file +@import "welcome-popup"; +@import "oauth.wizard"; \ No newline at end of file diff --git a/css/backoffice/pages/_oauth.wizard.scss b/css/backoffice/pages/_oauth.wizard.scss new file mode 100644 index 000000000..04fb33089 --- /dev/null +++ b/css/backoffice/pages/_oauth.wizard.scss @@ -0,0 +1,16 @@ +.ibo-oauth-wizard .ibo-panel--body{ + .ibo-oauth-wizard--form--container{ + display: flex; + flex-direction: row; + flex-grow: 1; + } + .ibo-oauth-wizard--form { + + } + .ibo-oauth-wizard--illustration svg{ + max-height: 400px; + } +} +#ibo-oauth-wizard--conf--result{ + white-space: pre-wrap +} diff --git a/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss b/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss index a8b2e550b..d6135be7b 100644 --- a/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss +++ b/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss @@ -202,7 +202,7 @@ $ibo-tab-container--tab-header--text-color: $ibo-color-grey-50; $ibo-vendors-datatables--row-highlight--colors:('red': ($ibo-color-red-700),'danger': ($ibo-color-red-700),'alert': ($ibo-color-red-700),'orange': ($ibo-color-orange-700),'warning': (#d08770),'blue': (#5e81ac),'info': (#5e81ac),); -$ibo-vendors-jqueryui--ui-dialog--background-color: $ibo-color-grey-800;jquer +$ibo-vendors-jqueryui--ui-dialog--background-color: $ibo-color-grey-800; $ibo-vendors-jqueryui--ui-dialog-titlebar--border-bottom: solid 1px $ibo-color-grey-500; diff --git a/lib/autoload.php b/lib/autoload.php index 460e67535..f1eeef5ab 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -2,6 +2,11 @@ // autoload.php @generated by Composer +if (PHP_VERSION_ID < 50600) { + echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; + exit(1); +} + require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader(); diff --git a/lib/composer/ClassLoader.php b/lib/composer/ClassLoader.php index 0cd6055d1..afef3fa2a 100644 --- a/lib/composer/ClassLoader.php +++ b/lib/composer/ClassLoader.php @@ -149,7 +149,7 @@ class ClassLoader /** * @return string[] Array of classname => path - * @psalm-var array + * @psalm-return array */ public function getClassMap() { diff --git a/lib/composer/InstalledVersions.php b/lib/composer/InstalledVersions.php index d50e0c9fc..7c5502ca4 100644 --- a/lib/composer/InstalledVersions.php +++ b/lib/composer/InstalledVersions.php @@ -24,21 +24,8 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { - /** - * @var mixed[]|null - * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null - */ private static $installed; - - /** - * @var bool|null - */ private static $canGetVendors; - - /** - * @var array[] - * @psalm-var array}> - */ private static $installedByVendor = array(); /** diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 2d1ad6621..13a11dfd1 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -2,7 +2,7 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( @@ -456,6 +456,7 @@ return array( 'DownloadPage' => $baseDir . '/sources/Application/WebPage/DownloadPage.php', 'EMail' => $baseDir . '/core/email.class.inc.php', 'EMailLaminas' => $baseDir . '/sources/Core/Email/EmailLaminas.php', + 'EmailSwiftMailer' => $baseDir . '/sources/Core/Email/EmailSwiftMailer.php', 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'ErrorPage' => $baseDir . '/sources/Application/WebPage/ErrorPage.php', 'Event' => $baseDir . '/core/event.class.inc.php', @@ -1391,6 +1392,8 @@ return array( 'StimulusInternal' => $baseDir . '/core/stimulus.class.inc.php', 'StimulusUserAction' => $baseDir . '/core/stimulus.class.inc.php', 'Str' => $baseDir . '/core/MyHelpers.class.inc.php', + 'Swift_LogFileTransport' => $baseDir . '/sources/Core/Email/EmailSwiftMailer.php', + 'Swift_Transport_LogFileTransport' => $baseDir . '/sources/Core/Email/EmailSwiftMailer.php', 'Symfony\\Bridge\\Twig\\AppVariable' => $vendorDir . '/symfony/twig-bridge/AppVariable.php', 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => $vendorDir . '/symfony/twig-bridge/Command/DebugCommand.php', 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => $vendorDir . '/symfony/twig-bridge/Command/LintCommand.php', diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php index 316c670f4..278b3f2e7 100644 --- a/lib/composer/autoload_files.php +++ b/lib/composer/autoload_files.php @@ -2,23 +2,23 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', - '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', - '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', + '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', ); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php index d12922d08..e6117c750 100644 --- a/lib/composer/autoload_namespaces.php +++ b/lib/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 00354f093..eca3dbac6 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -2,7 +2,7 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index cc554d8d1..dda4f956d 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -25,33 +25,20 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; $includePaths[] = get_include_path(); set_include_path(implode(PATH_SEPARATOR, $includePaths)); - $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); - } else { - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } + $includeFiles = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; foreach ($includeFiles as $fileIdentifier => $file) { composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file); } @@ -60,11 +47,16 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f } } +/** + * @param string $fileIdentifier + * @param string $file + * @return void + */ function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; } } diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 988d5803c..ba3725038 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -8,20 +8,20 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', - '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', - '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', ); public static $prefixLengthsPsr4 = array ( @@ -801,6 +801,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'DownloadPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/DownloadPage.php', 'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php', 'EMailLaminas' => __DIR__ . '/../..' . '/sources/Core/Email/EmailLaminas.php', + 'EmailSwiftMailer' => __DIR__ . '/../..' . '/sources/Core/Email/EmailSwiftMailer.php', 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'ErrorPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/ErrorPage.php', 'Event' => __DIR__ . '/../..' . '/core/event.class.inc.php', @@ -1736,6 +1737,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'StimulusInternal' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', 'StimulusUserAction' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', 'Str' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php', + 'Swift_LogFileTransport' => __DIR__ . '/../..' . '/sources/Core/Email/EmailSwiftMailer.php', + 'Swift_Transport_LogFileTransport' => __DIR__ . '/../..' . '/sources/Core/Email/EmailSwiftMailer.php', 'Symfony\\Bridge\\Twig\\AppVariable' => __DIR__ . '/..' . '/symfony/twig-bridge/AppVariable.php', 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/DebugCommand.php', 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/LintCommand.php', diff --git a/lib/composer/include_paths.php b/lib/composer/include_paths.php index d4fb96718..af33c1491 100644 --- a/lib/composer/include_paths.php +++ b/lib/composer/include_paths.php @@ -2,7 +2,7 @@ // include_paths.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/sources/Core/Email/EmailSwiftMailer.php b/sources/Core/Email/EmailSwiftMailer.php new file mode 100644 index 000000000..844a13a36 --- /dev/null +++ b/sources/Core/Email/EmailSwiftMailer.php @@ -0,0 +1,545 @@ + + + +/** + * Send an email (abstraction for synchronous/asynchronous modes) + * + * @copyright Copyright (C) 2010-2016 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +use Pelago\Emogrifier\CssInliner; +use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter; +use Pelago\Emogrifier\HtmlProcessor\HtmlPruner; + +Swift_Preferences::getInstance()->setCharset('UTF-8'); + +class EmailSwiftMailer extends EMail +{ + protected static $m_oConfig = null; + protected $m_aData; // For storing data to serialize + + public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE) + { + if (is_null(self::$m_oConfig)) + { + self::$m_oConfig = new Config($sConfigFile); + } + } + + protected $m_oMessage; + + /** @noinspection PhpMissingParentConstructorInspection */ + public function __construct() + { + $this->m_aData = array(); + $this->m_oMessage = new Swift_Message(); + $this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label')); + } + + /** + * Custom serialization method + * No longer use the brute force "serialize" method since + * 1) It does not work with binary attachments (since they cannot be stored in a UTF-8 text field) + * 2) The size tends to be quite big (sometimes ten times the size of the email) + */ + public function SerializeV2() + { + return serialize($this->m_aData); + } + + /** + * Custom de-serialization method + * @param string $sSerializedMessage The serialized representation of the message + */ + static public function UnSerializeV2($sSerializedMessage) + { + $aData = unserialize($sSerializedMessage); + $oMessage = new Email(); + + if (array_key_exists('body', $aData)) + { + $oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']); + } + if (array_key_exists('message_id', $aData)) + { + $oMessage->SetMessageId($aData['message_id']); + } + if (array_key_exists('bcc', $aData)) + { + $oMessage->SetRecipientBCC($aData['bcc']); + } + if (array_key_exists('cc', $aData)) + { + $oMessage->SetRecipientCC($aData['cc']); + } + if (array_key_exists('from', $aData)) + { + $oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']); + } + if (array_key_exists('reply_to', $aData)) + { + $oMessage->SetRecipientReplyTo($aData['reply_to']); + } + if (array_key_exists('to', $aData)) + { + $oMessage->SetRecipientTO($aData['to']); + } + if (array_key_exists('subject', $aData)) + { + $oMessage->SetSubject($aData['subject']); + } + + + if (array_key_exists('headers', $aData)) + { + foreach($aData['headers'] as $sKey => $sValue) + { + $oMessage->AddToHeader($sKey, $sValue); + } + } + if (array_key_exists('parts', $aData)) + { + foreach($aData['parts'] as $aPart) + { + $oMessage->AddPart($aPart['text'], $aPart['mimeType']); + } + } + if (array_key_exists('attachments', $aData)) + { + foreach($aData['attachments'] as $aAttachment) + { + $oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']); + } + } + return $oMessage; + } + + protected function SendAsynchronous(&$aIssues, $oLog = null) + { + try + { + AsyncSendEmail::AddToQueue($this, $oLog); + } + catch(Exception $e) + { + $aIssues = array($e->GetMessage()); + return EMAIL_SEND_ERROR; + } + $aIssues = array(); + return EMAIL_SEND_PENDING; + } + + public static function GetMailer() + { + return new EmailSwiftMailer(); + } + + protected function SendSynchronous(&$aIssues, $oLog = null) + { + // If the body of the message is in HTML, embed all images based on attachments + $this->EmbedInlineImages(); + + $this->LoadConfig(); + + $sTransport = self::$m_oConfig->Get('email_transport'); + switch ($sTransport) + { + case 'SMTP': + $sHost = self::$m_oConfig->Get('email_transport_smtp.host'); + $sPort = self::$m_oConfig->Get('email_transport_smtp.port'); + $sEncryption = self::$m_oConfig->Get('email_transport_smtp.encryption'); + $sUserName = self::$m_oConfig->Get('email_transport_smtp.username'); + $sPassword = self::$m_oConfig->Get('email_transport_smtp.password'); + + $oTransport = new Swift_SmtpTransport($sHost, $sPort, $sEncryption); + if (strlen($sUserName) > 0) + { + $oTransport->setUsername($sUserName); + $oTransport->setPassword($sPassword); + } + break; + + case 'Null': + $oTransport = new Swift_NullTransport(); + break; + + case 'LogFile': + $oTransport = new Swift_LogFileTransport(new Swift_Events_SimpleEventDispatcher()); + $oTransport->setLogFile(APPROOT.'log/mail.log'); + break; + + case 'PHPMail': + default: + $oTransport = new Swift_SendmailTransport(); + } + + $oMailer = new Swift_Mailer($oTransport); + + $aFailedRecipients = array(); + $this->m_oMessage->setMaxLineLength(0); + $oKPI = new ExecutionKPI(); + try + { + $iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients); + if ($iSent === 0) + { + // Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!! + IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients)); + $aIssues = array('Some recipients were invalid.'); + $oKPI->ComputeStats('Email Sent', 'Error received'); + return EMAIL_SEND_ERROR; + } + else + { + $aIssues = array(); + $oKPI->ComputeStats('Email Sent', 'Succeded'); + return EMAIL_SEND_OK; + } + } + catch (Exception $e) + { + $oKPI->ComputeStats('Email Sent', 'Error received'); + throw $e; + } + } + + /** + * Reprocess the body of the message (if it is an HTML message) + * to replace the URL of images based on attachments by a link + * to an embedded image (i.e. cid:....) + */ + protected function EmbedInlineImages() + { + if ($this->m_aData['body']['mimeType'] == 'text/html') + { + $oDOMDoc = new DOMDocument(); + $oDOMDoc->preserveWhitespace = true; + @$oDOMDoc->loadHTML(''.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified + + $oXPath = new DOMXPath($oDOMDoc); + $sXPath = '//img[@'.InlineImage::DOM_ATTR_ID.']'; + $oImagesList = $oXPath->query($sXPath); + + if ($oImagesList->length != 0) + { + foreach($oImagesList as $oImg) + { + $iAttId = $oImg->getAttribute(InlineImage::DOM_ATTR_ID); + $oAttachment = MetaModel::GetObject('InlineImage', $iAttId, false, true /* Allow All Data */); + if ($oAttachment) + { + $sImageSecret = $oImg->getAttribute('data-img-secret'); + $sAttachmentSecret = $oAttachment->Get('secret'); + if ($sImageSecret !== $sAttachmentSecret) + { + // @see N°1921 + // If copying from another iTop we could get an IMG pointing to an InlineImage with wrong secret + continue; + } + + $oDoc = $oAttachment->Get('contents'); + $oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType()); + $sCid = $this->m_oMessage->embed($oSwiftImage); + $oImg->setAttribute('src', $sCid); + } + } + } + $sHtmlBody = $oDOMDoc->saveHTML(); + $this->m_oMessage->setBody($sHtmlBody, 'text/html', 'UTF-8'); + } + } + + public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null) + { + //select a default sender if none is provided. + if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){ + $this->SetRecipientFrom($this->m_aData['to']); + } + + if ($bForceSynchronous) + { + return $this->SendSynchronous($aIssues, $oLog); + } + else + { + $bConfigASYNC = MetaModel::GetConfig()->Get('email_asynchronous'); + if ($bConfigASYNC) + { + return $this->SendAsynchronous($aIssues, $oLog); + } + else + { + return $this->SendSynchronous($aIssues, $oLog); + } + } + } + + public function AddToHeader($sKey, $sValue) + { + if (!array_key_exists('headers', $this->m_aData)) + { + $this->m_aData['headers'] = array(); + } + $this->m_aData['headers'][$sKey] = $sValue; + + if (strlen($sValue) > 0) + { + $oHeaders = $this->m_oMessage->getHeaders(); + switch(strtolower($sKey)) + { + case 'return-path': + $this->m_oMessage->setReturnPath($sValue); + break; + + default: + $oHeaders->addTextHeader($sKey, $sValue); + } + } + } + + public function SetMessageId($sId) + { + $this->m_aData['message_id'] = $sId; + + // Note: Swift will add the angle brackets for you + // so let's remove the angle brackets if present, for historical reasons + $sId = str_replace(array('<', '>'), '', $sId); + + $oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID'); + $oMsgId->SetId($sId); + } + + public function SetReferences($sReferences) + { + $this->AddToHeader('References', $sReferences); + } + + public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null) + { + if (($sMimeType === 'text/html') && ($sCustomStyles !== null)) + { + $oDomDocument = CssInliner::fromHtml($sBody)->inlineCss($sCustomStyles)->getDomDocument(); + HtmlPruner::fromDomDocument($oDomDocument)->removeElementsWithDisplayNone(); + $sBody = CssToAttributeConverter::fromDomDocument($oDomDocument)->convertCssToVisualAttributes()->render(); // Adds html/body tags if not already present + } + $this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType); + $this->m_oMessage->setBody($sBody, $sMimeType); + } + + public function AddPart($sText, $sMimeType = 'text/html') + { + if (!array_key_exists('parts', $this->m_aData)) + { + $this->m_aData['parts'] = array(); + } + $this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType); + $this->m_oMessage->addPart($sText, $sMimeType); + } + + public function AddAttachment($data, $sFileName, $sMimeType) + { + if (!array_key_exists('attachments', $this->m_aData)) + { + $this->m_aData['attachments'] = array(); + } + $this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType); + $this->m_oMessage->attach(new Swift_Attachment($data, $sFileName, $sMimeType)); + } + + public function SetSubject($sSubject) + { + $this->m_aData['subject'] = $sSubject; + $this->m_oMessage->setSubject($sSubject); + } + + public function GetSubject() + { + return $this->m_oMessage->getSubject(); + } + + /** + * Helper to transform and sanitize addresses + * - get rid of empty addresses + */ + protected function AddressStringToArray($sAddressCSVList) + { + $aAddresses = array(); + foreach(explode(',', $sAddressCSVList) as $sAddress) + { + $sAddress = trim($sAddress); + if (strlen($sAddress) > 0) + { + $aAddresses[] = $sAddress; + } + } + return $aAddresses; + } + + public function SetRecipientTO($sAddress) + { + $this->m_aData['to'] = $sAddress; + if (!empty($sAddress)) + { + $aAddresses = $this->AddressStringToArray($sAddress); + $this->m_oMessage->setTo($aAddresses); + } + } + + public function GetRecipientTO($bAsString = false) + { + $aRes = $this->m_oMessage->getTo(); + if ($aRes === null) + { + // There is no "To" header field + $aRes = array(); + } + if ($bAsString) + { + $aStrings = array(); + foreach ($aRes as $sEmail => $sName) + { + if (is_null($sName)) + { + $aStrings[] = $sEmail; + } + else + { + $sName = str_replace(array('<', '>'), '', $sName); + $aStrings[] = "$sName <$sEmail>"; + } + } + return implode(', ', $aStrings); + } + else + { + return $aRes; + } + } + + public function SetRecipientCC($sAddress) + { + $this->m_aData['cc'] = $sAddress; + if (!empty($sAddress)) + { + $aAddresses = $this->AddressStringToArray($sAddress); + $this->m_oMessage->setCc($aAddresses); + } + } + + public function SetRecipientBCC($sAddress) + { + $this->m_aData['bcc'] = $sAddress; + if (!empty($sAddress)) + { + $aAddresses = $this->AddressStringToArray($sAddress); + $this->m_oMessage->setBcc($aAddresses); + } + } + + public function SetRecipientFrom($sAddress, $sLabel = '') + { + $this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel); + if ($sLabel != '') + { + $this->m_oMessage->setFrom(array($sAddress => $sLabel)); + } + else if (!empty($sAddress)) + { + $this->m_oMessage->setFrom($sAddress); + } + } + + public function SetRecipientReplyTo($sAddress, $sLabel = '') + { + $this->m_aData['reply_to'] = $sAddress; + if (!empty($sAddress)) { + $this->m_oMessage->setReplyTo($sAddress); + } + } + +} + +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Extension to SwiftMailer: "debug" transport that pretends messages have been sent, + * but just log them to a file. + * + * @package Swift + * @author Denis Flaven + */ +class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport +{ + protected $sLogFile; + + /** + * @inheritDoc + */ + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) + { + $hFile = @fopen($this->sLogFile, 'a'); + if ($hFile) { + $sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n"; + $sTxt .= $message->toString()."\n"; + + @fwrite($hFile, $sTxt); + @fclose($hFile); + } + + return parent::send($message, $failedRecipients); + } + + public function setLogFile($sFilename) + { + $this->sLogFile = $sFilename; + } +} + +/** + * Pretends messages have been sent, but just log them to a file. + * + * @package Swift + * @author Denis Flaven + */ +class Swift_LogFileTransport extends Swift_Transport_LogFileTransport +{ + /** + * Create a new LogFileTransport. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher) + { + parent::__construct($eventDispatcher); + call_user_func_array( + array($this, 'Swift_Transport_LogFileTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.null') + ); + } + + /** + * Create a new LogFileTransport instance. + * + * @return Swift_LogFileTransport + */ + public static function newInstance() + { + return new self(new Swift_Events_SimpleEventDispatcher()); + } +} \ No newline at end of file