mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
N°3169 - Add feature to connect Gsuite mail box with OAuth
N°2504 - Add feature to connect Office mail box with OAuth2 for Microsoft Graph N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth * 2.7 migration
This commit is contained in:
@@ -555,6 +555,46 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'email_transport_smtp.oauth.provider' => [
|
||||
'type' => 'string',
|
||||
'description' => 'TODO',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.oauth.client_id' => [
|
||||
'type' => 'string',
|
||||
'description' => 'TODO',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.oauth.client_secret' => [
|
||||
'type' => 'string',
|
||||
'description' => 'TODO',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.oauth.access_token' => [
|
||||
'type' => 'string',
|
||||
'description' => 'TODO',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.oauth.refresh_token' => [
|
||||
'type' => 'string',
|
||||
'description' => 'TODO',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_css' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'CSS that will override the standard stylesheet used for the notifications',
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
* @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');
|
||||
use Combodo\iTop\Core\Email\EmailFactory;
|
||||
|
||||
|
||||
define ('EMAIL_SEND_OK', 0);
|
||||
@@ -37,29 +33,16 @@ define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
class EMail
|
||||
{
|
||||
protected $oMailer;
|
||||
|
||||
// Serialization formats
|
||||
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
|
||||
|
||||
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;
|
||||
|
||||
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'));
|
||||
$this->oMailer = EmailFactory::GetMailer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,481 +53,97 @@ class EMail
|
||||
*/
|
||||
public function SerializeV2()
|
||||
{
|
||||
return serialize($this->m_aData);
|
||||
return $this->oMailer->SerializeV2();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom de-serialization method
|
||||
*
|
||||
* @param string $sSerializedMessage The serialized representation of the message
|
||||
*
|
||||
* @return \Email
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
$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('<?xml encoding="UTF-8"?>'.$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');
|
||||
}
|
||||
return EmailFactory::GetMailer()::UnSerializeV2($sSerializedMessage);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return $this->oMailer->Send($aIssues, $bForceSynchronous, $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);
|
||||
}
|
||||
}
|
||||
$this->oMailer->AddToHeader($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);
|
||||
$this->oMailer->SetMessageId($sId);
|
||||
}
|
||||
|
||||
public function SetReferences($sReferences)
|
||||
{
|
||||
$this->AddToHeader('References', $sReferences);
|
||||
$this->oMailer->SetReferences($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);
|
||||
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
|
||||
}
|
||||
|
||||
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);
|
||||
$this->oMailer->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));
|
||||
$this->oMailer->AddAttachment($data, $sFileName, $sMimeType);
|
||||
}
|
||||
|
||||
public function SetSubject($sSubject)
|
||||
{
|
||||
$this->m_aData['subject'] = $sSubject;
|
||||
$this->m_oMessage->setSubject($sSubject);
|
||||
$this->oMailer->SetSubject($sSubject);
|
||||
}
|
||||
|
||||
public function GetSubject()
|
||||
{
|
||||
return $this->m_oMessage->getSubject();
|
||||
return $this->oMailer->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);
|
||||
}
|
||||
$this->oMailer->SetRecipientTO($sAddress);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return $this->oMailer->GetRecipientTO($bAsString);
|
||||
}
|
||||
|
||||
public function SetRecipientCC($sAddress)
|
||||
{
|
||||
$this->m_aData['cc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setCc($aAddresses);
|
||||
}
|
||||
$this->oMailer->SetRecipientCC($sAddress);
|
||||
}
|
||||
|
||||
public function SetRecipientBCC($sAddress)
|
||||
{
|
||||
$this->m_aData['bcc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setBcc($aAddresses);
|
||||
}
|
||||
$this->oMailer->SetRecipientBCC($sAddress);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
$this->oMailer->SetRecipientFrom($sAddress, $sLabel);
|
||||
}
|
||||
|
||||
public function SetRecipientReplyTo($sAddress)
|
||||
{
|
||||
$this->m_aData['reply_to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setReplyTo($sAddress);
|
||||
}
|
||||
$this->oMailer->SetRecipientReplyTo($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();
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class ClassLoader
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-return array<string, string>
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
|
||||
@@ -160,6 +160,7 @@ return array(
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderFactory' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderFactory.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientResultDisplayConf' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientResultDisplayConf.php',
|
||||
'Combodo\\iTop\\Core\\Email\\EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.php',
|
||||
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php',
|
||||
@@ -246,6 +247,7 @@ return array(
|
||||
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Doctrine\\Common\\Lexer\\AbstractLexer' => $vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php',
|
||||
'EMail' => $baseDir . '/core/email.class.inc.php',
|
||||
'EMailLaminas' => $baseDir . '/sources/Core/Email/EmailLaminas.php',
|
||||
'Egulias\\EmailValidator\\EmailLexer' => $vendorDir . '/egulias/email-validator/src/EmailLexer.php',
|
||||
'Egulias\\EmailValidator\\EmailParser' => $vendorDir . '/egulias/email-validator/src/EmailParser.php',
|
||||
'Egulias\\EmailValidator\\EmailValidator' => $vendorDir . '/egulias/email-validator/src/EmailValidator.php',
|
||||
@@ -311,7 +313,6 @@ return array(
|
||||
'Egulias\\EmailValidator\\Warning\\QuotedString' => $vendorDir . '/egulias/email-validator/src/Warning/QuotedString.php',
|
||||
'Egulias\\EmailValidator\\Warning\\TLD' => $vendorDir . '/egulias/email-validator/src/Warning/TLD.php',
|
||||
'Egulias\\EmailValidator\\Warning\\Warning' => $vendorDir . '/egulias/email-validator/src/Warning/Warning.php',
|
||||
'EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.php',
|
||||
'EmailSwiftMailer' => $baseDir . '/sources/Core/Email/EmailSwiftMailer.php',
|
||||
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'ErrorPage' => $baseDir . '/application/errorpage.class.inc.php',
|
||||
@@ -1265,8 +1266,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 . '/core/email.class.inc.php',
|
||||
'Swift_Transport_LogFileTransport' => $baseDir . '/core/email.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',
|
||||
|
||||
@@ -60,16 +60,11 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +528,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderFactory' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderFactory.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientResultDisplayConf' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientResultDisplayConf.php',
|
||||
'Combodo\\iTop\\Core\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php',
|
||||
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php',
|
||||
@@ -614,6 +615,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Doctrine\\Common\\Lexer\\AbstractLexer' => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php',
|
||||
'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php',
|
||||
'EMailLaminas' => __DIR__ . '/../..' . '/sources/Core/Email/EmailLaminas.php',
|
||||
'Egulias\\EmailValidator\\EmailLexer' => __DIR__ . '/..' . '/egulias/email-validator/src/EmailLexer.php',
|
||||
'Egulias\\EmailValidator\\EmailParser' => __DIR__ . '/..' . '/egulias/email-validator/src/EmailParser.php',
|
||||
'Egulias\\EmailValidator\\EmailValidator' => __DIR__ . '/..' . '/egulias/email-validator/src/EmailValidator.php',
|
||||
@@ -679,7 +681,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'Egulias\\EmailValidator\\Warning\\QuotedString' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/QuotedString.php',
|
||||
'Egulias\\EmailValidator\\Warning\\TLD' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/TLD.php',
|
||||
'Egulias\\EmailValidator\\Warning\\Warning' => __DIR__ . '/..' . '/egulias/email-validator/src/Warning/Warning.php',
|
||||
'EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php',
|
||||
'EmailSwiftMailer' => __DIR__ . '/../..' . '/sources/Core/Email/EmailSwiftMailer.php',
|
||||
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'ErrorPage' => __DIR__ . '/../..' . '/application/errorpage.class.inc.php',
|
||||
@@ -1633,8 +1634,8 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'StimulusInternal' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php',
|
||||
'StimulusUserAction' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php',
|
||||
'Str' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php',
|
||||
'Swift_LogFileTransport' => __DIR__ . '/../..' . '/core/email.class.inc.php',
|
||||
'Swift_Transport_LogFileTransport' => __DIR__ . '/../..' . '/core/email.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',
|
||||
|
||||
@@ -107,9 +107,9 @@ function CheckEmailSetting($oP)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'SMTP':
|
||||
$oP->info("iTop is configured to use the <b>SMTP</b> transport.");
|
||||
$oP->info("iTop is configured to use the <b>$sTransport</b> transport.");
|
||||
$sHost = MetaModel::GetConfig()->Get('email_transport_smtp.host');
|
||||
$sPort = MetaModel::GetConfig()->Get('email_transport_smtp.port');
|
||||
$sEncryption = MetaModel::GetConfig()->Get('email_transport_smtp.encryption');
|
||||
@@ -124,7 +124,26 @@ function CheckEmailSetting($oP)
|
||||
$oP->warning("The default settings may not be suitable for your environment. You may want to adjust these values by editing iTop's configuration file (".utils::GetConfigFilePathRelative().").");
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'SMTP_OAuth':
|
||||
$oP->info("iTop is configured to use the <b>$sTransport</b> transport.");
|
||||
$sHost = MetaModel::GetConfig()->Get('email_transport_smtp.host');
|
||||
$sPort = MetaModel::GetConfig()->Get('email_transport_smtp.port');
|
||||
$sEncryption = MetaModel::GetConfig()->Get('email_transport_smtp.encryption');
|
||||
$sDisplayEncryption = empty($sEncryption) ? '<em>no encryption</em> ' : $sEncryption;
|
||||
$sUserName = MetaModel::GetConfig()->Get('email_transport_smtp.username');
|
||||
$sDisplayUserName = empty($sUserName) ? '<em>no user</em> ' : $sUserName;
|
||||
$sProvider = MetaModel::GetConfig()->Get('email_transport_smtp.oauth.provider');
|
||||
$sDisplayProvider = empty($sProvider) ? '<em>no Provider</em> ' : $sProvider;
|
||||
$sClientID = MetaModel::GetConfig()->Get('email_transport_smtp.oauth.client_id');
|
||||
$sDisplayClientID = empty($sClientID) ? '<em>no password</em> ' : $sClientID;
|
||||
$oP->info("SMTP configuration (from config-itop.php): host: $sHost, port: $sPort, provider: $sDisplayProvider, user: $sDisplayUserName, client id: $sDisplayClientID, encryption: $sDisplayEncryption.");
|
||||
if (($sHost == 'localhost') && ($sPort == '25') && ($sUserName == '') && ($sClientID == '') && ($sProvider == '')) {
|
||||
$oP->warning("The default settings may not be suitable for your environment. You may want to adjust these values by editing iTop's configuration file (".utils::GetConfigFilePathRelative().').');
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'Null':
|
||||
$oP->warning("iTop is configured to use the <b>Null</b> transport: emails sending will have no effect.");
|
||||
$bRet = false;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Combodo\iTop\Controller\OAuth;
|
||||
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderAbstract;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
|
||||
use utils;
|
||||
|
||||
@@ -36,13 +35,17 @@ class OAuthAjaxController extends Controller
|
||||
$sAdditional = utils::ReadParam('additional', '', false, 'raw');
|
||||
|
||||
$sRedirectUrlQuery = parse_url($sRedirectUrl)['query'];
|
||||
// TODO: Needs to handle mail to ticket part too
|
||||
$aOAuthResultDisplayClasses = ['\Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientResultDisplayConf'];
|
||||
|
||||
$aOAuthResultDisplayClasses[] = '\Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientResultDisplayConf';
|
||||
if (class_exists('Combodo\iTop\Extension\Service\OAuthClientResultDisplayMailbox')) {
|
||||
$aOAuthResultDisplayClasses[] = 'Combodo\iTop\Extension\Service\OAuthClientResultDisplayMailbox';
|
||||
}
|
||||
|
||||
$aAdditional = [];
|
||||
parse_str($sAdditional, $aAdditional);
|
||||
|
||||
$sProviderClass = "\Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProvider".$sProvider;
|
||||
$sRedirectUrl = OAuthClientProviderAbstract::GetRedirectUri();
|
||||
// $sProviderClass = "\Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProvider".$sProvider;
|
||||
// $sRedirectUrl = OAuthClientProviderAbstract::GetRedirectUri();
|
||||
|
||||
$aQuery = [];
|
||||
parse_str($sRedirectUrlQuery, $aQuery);
|
||||
|
||||
@@ -9,11 +9,25 @@ namespace Combodo\iTop\Controller\OAuth;
|
||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderAbstract;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientResultDisplayConf;
|
||||
use Combodo\iTop\Extension\Service\OAuthClientResultDisplayMailbox;
|
||||
use Dict;
|
||||
use utils;
|
||||
|
||||
class OAuthWizardController extends Controller
|
||||
{
|
||||
public function __construct($sViewPath, $sModuleName = 'core')
|
||||
{
|
||||
$aAdditionalPaths = [];
|
||||
|
||||
// Add extensions' template path
|
||||
// TODO Rewrite in 3.1 with utils::GetClassesForInterface('Combodo\iTop\Core\Authentication\Client\OAuth\IOAuthClientResultDisplay', ...)
|
||||
if (class_exists('Combodo\iTop\Extension\Service\OAuthClientResultDisplayMailbox')) {
|
||||
$aAdditionalPaths[] = utils::GetAbsoluteModulePath('combodo-oauth-email-synchro').'templates';
|
||||
}
|
||||
|
||||
parent::__construct($sViewPath, $sModuleName, $aAdditionalPaths);
|
||||
}
|
||||
|
||||
public function OperationWizard()
|
||||
{
|
||||
$aParams = [];
|
||||
@@ -44,9 +58,10 @@ class OAuthWizardController extends Controller
|
||||
];
|
||||
|
||||
// TODO: Needs to handle mail to ticket part too
|
||||
$aParams['aAdditionalBlocks'] = [
|
||||
OAuthClientResultDisplayConf::GetResultDisplayTemplate(),
|
||||
];
|
||||
$aParams['aAdditionalBlocks'][] = OAuthClientResultDisplayConf::GetResultDisplayTemplate();
|
||||
if (class_exists('Combodo\iTop\Extension\Service\OAuthClientResultDisplayMailbox')) {
|
||||
$aParams['aAdditionalBlocks'][] = OAuthClientResultDisplayMailbox::GetResultDisplayTemplate();
|
||||
}
|
||||
|
||||
$this->DisplayPage($aParams);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Combodo\iTop\Core\Authentication\Client\OAuth;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
|
||||
interface IOAuthClientResultDisplay{
|
||||
public static function GetResultDisplayBlock();
|
||||
//public static function GetResultDisplayBlock();
|
||||
public static function GetResultDisplayScript($sClientId, $sClientSecret, $sVendor, AccessToken $oAccessToken);
|
||||
|
||||
public static function GetResultDisplayTemplate();
|
||||
|
||||
@@ -90,7 +90,7 @@ class Oauth extends Login
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
$sResponse = $this->_receive(60);
|
||||
$sResponse = $this->_receive(30);
|
||||
|
||||
IssueLog::Debug("SMTP Oauth receiving $sResponse", static::LOG_CHANNEL);
|
||||
|
||||
@@ -100,7 +100,7 @@ class Oauth extends Login
|
||||
} else {
|
||||
if (preg_match('/Unauthorized/i', $sResponse) ||
|
||||
preg_match('/Rejected/i', $sResponse) ||
|
||||
preg_match('/^(535|432|454|534|500|530|538)/', $sResponse)) {
|
||||
preg_match('/^(535|432|454|534|500|530|538|334)/', $sResponse)) {
|
||||
IssueLog::Error('Unable to authenticate for outgoing mails for provider '.self::$oProvider::GetVendorName()." Error: $sResponse", static::LOG_CHANNEL);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Core\Email;
|
||||
|
||||
use EMailLaminas;
|
||||
use EmailSwiftMailer;
|
||||
use utils;
|
||||
|
||||
class EmailFactory
|
||||
{
|
||||
public static function GetMailer()
|
||||
{
|
||||
$sTransport = utils::GetConfig()->Get('email_transport');
|
||||
if ($sTransport == 'SMTP_OAuth') {
|
||||
return EMailLaminas::GetMailer();
|
||||
}
|
||||
|
||||
return EmailSwiftMailer::GetMailer();
|
||||
}
|
||||
}
|
||||
@@ -38,24 +38,19 @@ use Pelago\Emogrifier\CssInliner;
|
||||
use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter;
|
||||
use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
|
||||
|
||||
define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
class EMail
|
||||
class EMailLaminas
|
||||
{
|
||||
// Serialization formats
|
||||
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
|
||||
|
||||
|
||||
protected static $m_oConfig = null;
|
||||
protected $m_aData; // For storing data to serialize
|
||||
|
||||
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
|
||||
public static function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
|
||||
{
|
||||
if (is_null(self::$m_oConfig))
|
||||
{
|
||||
if (is_null(self::$m_oConfig)) {
|
||||
self::$m_oConfig = new Config($sConfigFile);
|
||||
}
|
||||
}
|
||||
@@ -95,171 +90,160 @@ class EMail
|
||||
{
|
||||
$aData = unserialize($sSerializedMessage);
|
||||
$oMessage = new Email();
|
||||
|
||||
if (array_key_exists('body', $aData))
|
||||
{
|
||||
|
||||
if (array_key_exists('body', $aData)) {
|
||||
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
|
||||
}
|
||||
if (array_key_exists('message_id', $aData))
|
||||
{
|
||||
if (array_key_exists('message_id', $aData)) {
|
||||
$oMessage->SetMessageId($aData['message_id']);
|
||||
}
|
||||
if (array_key_exists('bcc', $aData))
|
||||
{
|
||||
if (array_key_exists('bcc', $aData)) {
|
||||
$oMessage->SetRecipientBCC($aData['bcc']);
|
||||
}
|
||||
if (array_key_exists('cc', $aData))
|
||||
{
|
||||
if (array_key_exists('cc', $aData)) {
|
||||
$oMessage->SetRecipientCC($aData['cc']);
|
||||
}
|
||||
if (array_key_exists('from', $aData))
|
||||
{
|
||||
if (array_key_exists('from', $aData)) {
|
||||
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
|
||||
}
|
||||
if (array_key_exists('reply_to', $aData))
|
||||
{
|
||||
if (array_key_exists('reply_to', $aData)) {
|
||||
$oMessage->SetRecipientReplyTo($aData['reply_to']['address'], $aData['reply_to']['label']);
|
||||
}
|
||||
if (array_key_exists('to', $aData))
|
||||
{
|
||||
if (array_key_exists('to', $aData)) {
|
||||
$oMessage->SetRecipientTO($aData['to']);
|
||||
}
|
||||
if (array_key_exists('subject', $aData))
|
||||
{
|
||||
if (array_key_exists('subject', $aData)) {
|
||||
$oMessage->SetSubject($aData['subject']);
|
||||
}
|
||||
|
||||
if (array_key_exists('headers', $aData))
|
||||
{
|
||||
foreach($aData['headers'] as $sKey => $sValue)
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
||||
protected function SendAsynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
AsyncSendEmail::AddToQueue($this, $oLog);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
$aIssues = array($e->GetMessage());
|
||||
|
||||
return EMAIL_SEND_ERROR;
|
||||
}
|
||||
$aIssues = array();
|
||||
|
||||
return EMAIL_SEND_PENDING;
|
||||
}
|
||||
|
||||
public static function GetMailer()
|
||||
{
|
||||
return new EMailLaminas();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function SendSynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
|
||||
|
||||
$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');
|
||||
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 Smtp();
|
||||
$aOptions= [
|
||||
'host' => $sHost,
|
||||
'port' => $sPort,
|
||||
'connection_class' => 'login',
|
||||
'connection_config' => [
|
||||
'ssl' => $sEncryption,
|
||||
],
|
||||
];
|
||||
if (strlen($sUserName) > 0)
|
||||
{
|
||||
$aOptions['connection_config']['username'] = $sUserName;
|
||||
$aOptions['connection_config']['password'] = $sPassword;
|
||||
}
|
||||
$oOptions = new SmtpOptions($aOptions);
|
||||
$oTransport->setOptions($oOptions);
|
||||
break;
|
||||
case 'SMTP_OAuth':
|
||||
$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');
|
||||
$oTransport = new Smtp();
|
||||
$aOptions = [
|
||||
'host' => $sHost,
|
||||
'port' => $sPort,
|
||||
'connection_class' => 'login',
|
||||
'connection_config' => [
|
||||
'ssl' => $sEncryption,
|
||||
],
|
||||
];
|
||||
if (strlen($sUserName) > 0) {
|
||||
$aOptions['connection_config']['username'] = $sUserName;
|
||||
$aOptions['connection_config']['password'] = $sPassword;
|
||||
}
|
||||
$oOptions = new SmtpOptions($aOptions);
|
||||
$oTransport->setOptions($oOptions);
|
||||
break;
|
||||
case 'SMTP_OAuth':
|
||||
$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');
|
||||
|
||||
$oTransport = new Smtp();
|
||||
$aOptions= [
|
||||
'host' => $sHost,
|
||||
'port' => $sPort,
|
||||
'connection_class' => 'Laminas\Mail\Protocol\Smtp\Auth\Oauth',
|
||||
'connection_config' => [
|
||||
'ssl' => $sEncryption,
|
||||
],
|
||||
];
|
||||
if (strlen($sUserName) > 0)
|
||||
{
|
||||
$aOptions['connection_config']['username'] = $sUserName;
|
||||
}
|
||||
$oOptions = new SmtpOptions($aOptions);
|
||||
$oTransport->setOptions($oOptions);
|
||||
$oTransport = new Smtp();
|
||||
$aOptions = [
|
||||
'host' => $sHost,
|
||||
'port' => $sPort,
|
||||
'connection_class' => 'Laminas\Mail\Protocol\Smtp\Auth\Oauth',
|
||||
'connection_config' => [
|
||||
'ssl' => $sEncryption,
|
||||
],
|
||||
];
|
||||
if (strlen($sUserName) > 0) {
|
||||
$aOptions['connection_config']['username'] = $sUserName;
|
||||
}
|
||||
$oOptions = new SmtpOptions($aOptions);
|
||||
$oTransport->setOptions($oOptions);
|
||||
|
||||
\Laminas\Mail\Protocol\Smtp\Auth\Oauth::setProvider(OAuthClientProviderFactory::getProviderForSMTP());
|
||||
break;
|
||||
case 'Null':
|
||||
$oTransport = new Smtp();
|
||||
break;
|
||||
|
||||
case 'LogFile':
|
||||
$oTransport = new File();
|
||||
$aOptions = new FileOptions([
|
||||
'path' => APPROOT.'log/mail.log',
|
||||
]);
|
||||
$oTransport->setOptions($aOptions);
|
||||
break;
|
||||
|
||||
case 'PHPMail':
|
||||
default:
|
||||
$oTransport = new Smtp();
|
||||
\Laminas\Mail\Protocol\Smtp\Auth\Oauth::setProvider(OAuthClientProviderFactory::getProviderForSMTP());
|
||||
break;
|
||||
case 'Null':
|
||||
$oTransport = new Smtp();
|
||||
break;
|
||||
|
||||
case 'LogFile':
|
||||
$oTransport = new File();
|
||||
$aOptions = new FileOptions([
|
||||
'path' => APPROOT.'log/mail.log',
|
||||
]);
|
||||
$oTransport->setOptions($aOptions);
|
||||
break;
|
||||
|
||||
case 'PHPMail':
|
||||
default:
|
||||
$oTransport = new Smtp();
|
||||
}
|
||||
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
try {
|
||||
$oTransport->send($this->m_oMessage);
|
||||
$aIssues = array();
|
||||
$oKPI->ComputeStats('Email Sent', 'Succeded');
|
||||
|
||||
return EMAIL_SEND_OK;
|
||||
}
|
||||
catch(Laminas\Mail\Transport\Exception\RuntimeException $e){
|
||||
catch (Laminas\Mail\Transport\Exception\RuntimeException $e) {
|
||||
IssueLog::Warning('Email sending failed: Some recipients were invalid');
|
||||
$aIssues = array('Some recipients were invalid.');
|
||||
$oKPI->ComputeStats('Email Sent', 'Error received');
|
||||
|
||||
return EMAIL_SEND_ERROR;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
$oKPI->ComputeStats('Email Sent', 'Error received');
|
||||
throw $e;
|
||||
}
|
||||
@@ -287,18 +271,14 @@ class EMail
|
||||
$oImagesList = $oXPath->query($sXPath);
|
||||
$oImagesContent = new \Laminas\Mime\Message();
|
||||
$aImagesParts = [];
|
||||
if ($oImagesList->length != 0)
|
||||
{
|
||||
foreach($oImagesList as $oImg)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if ($oAttachment) {
|
||||
$sImageSecret = $oImg->getAttribute('data-img-secret');
|
||||
$sAttachmentSecret = $oAttachment->Get('secret');
|
||||
if ($sImageSecret !== $sAttachmentSecret)
|
||||
{
|
||||
if ($sImageSecret !== $sAttachmentSecret) {
|
||||
// @see N°1921
|
||||
// If copying from another iTop we could get an IMG pointing to an InlineImage with wrong secret
|
||||
continue;
|
||||
@@ -314,7 +294,7 @@ class EMail
|
||||
$oNewAttachment->filename = $oDoc->GetFileName();
|
||||
$oNewAttachment->disposition = Mime::DISPOSITION_INLINE;
|
||||
$oNewAttachment->encoding = Mime::ENCODING_BASE64;
|
||||
|
||||
|
||||
$oImagesContent->addPart($oNewAttachment);
|
||||
$oImg->setAttribute('src', 'cid:'.$sCid);
|
||||
$aImagesParts[] = $oNewAttachment;
|
||||
@@ -322,29 +302,24 @@ class EMail
|
||||
}
|
||||
}
|
||||
$sBody = $oDOMDoc->saveHTML();
|
||||
|
||||
return $aImagesParts;
|
||||
}
|
||||
|
||||
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'])){
|
||||
if (empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])) {
|
||||
$this->SetRecipientFrom($this->m_aData['to']);
|
||||
}
|
||||
|
||||
if ($bForceSynchronous)
|
||||
{
|
||||
if ($bForceSynchronous) {
|
||||
return $this->SendSynchronous($aIssues, $oLog);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$bConfigASYNC = MetaModel::GetConfig()->Get('email_asynchronous');
|
||||
if ($bConfigASYNC)
|
||||
{
|
||||
if ($bConfigASYNC) {
|
||||
return $this->SendAsynchronous($aIssues, $oLog);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $this->SendSynchronous($aIssues, $oLog);
|
||||
}
|
||||
}
|
||||
@@ -352,17 +327,14 @@ class EMail
|
||||
|
||||
public function AddToHeader($sKey, $sValue)
|
||||
{
|
||||
if (!array_key_exists('headers', $this->m_aData))
|
||||
{
|
||||
if (!array_key_exists('headers', $this->m_aData)) {
|
||||
$this->m_aData['headers'] = array();
|
||||
}
|
||||
$this->m_aData['headers'][$sKey] = $sValue;
|
||||
|
||||
if (strlen($sValue) > 0)
|
||||
{
|
||||
|
||||
if (strlen($sValue) > 0) {
|
||||
$oHeaders = $this->m_oMessage->getHeaders();
|
||||
switch(strtolower($sKey))
|
||||
{
|
||||
switch (strtolower($sKey)) {
|
||||
case 'return-path':
|
||||
$this->m_oMessage->setReturnPath($sValue);
|
||||
break;
|
||||
@@ -376,7 +348,7 @@ class EMail
|
||||
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);
|
||||
@@ -405,7 +377,7 @@ class EMail
|
||||
|
||||
/**
|
||||
* Set current Email body and process inline images.
|
||||
*
|
||||
*
|
||||
* @param $sBody
|
||||
* @param string $sMimeType
|
||||
* @param $sCustomStyles
|
||||
@@ -419,7 +391,7 @@ class EMail
|
||||
{
|
||||
$oBody = new Laminas\Mime\Message();
|
||||
$aAdditionalParts = [];
|
||||
|
||||
|
||||
if (($sMimeType === Mime::TYPE_HTML) && ($sCustomStyles !== null)) {
|
||||
$oDomDocument = CssInliner::fromHtml($sBody)->inlineCss($sCustomStyles)->getDomDocument();
|
||||
HtmlPruner::fromDomDocument($oDomDocument)->removeElementsWithDisplayNone();
|
||||
@@ -428,22 +400,22 @@ class EMail
|
||||
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
|
||||
|
||||
// We don't want these modifications in m_aData['body'], otherwise it'll ruin asynchronous mail as they go through this method twice
|
||||
if ($sMimeType === Mime::TYPE_HTML){
|
||||
if ($sMimeType === Mime::TYPE_HTML) {
|
||||
$aAdditionalParts = $this->EmbedInlineImages($sBody);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add body content to as a new part
|
||||
$oNewPart = new Part($sBody);
|
||||
$oNewPart->encoding = Mime::ENCODING_8BIT;
|
||||
$oNewPart->type = $sMimeType;
|
||||
$oBody->addPart($oNewPart);
|
||||
|
||||
|
||||
// Add additional images as new body parts
|
||||
foreach ($aAdditionalParts as $oAdditionalPart) {
|
||||
$oBody->addPart($oAdditionalPart);
|
||||
}
|
||||
|
||||
if($oBody->isMultiPart()){
|
||||
if ($oBody->isMultiPart()) {
|
||||
$oContentTypeHeader = $this->m_oMessage->getHeaders();
|
||||
foreach ($oContentTypeHeader as $oHeader) {
|
||||
if (!$oHeader instanceof ContentType) {
|
||||
@@ -455,12 +427,13 @@ class EMail
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->m_oMessage->setBody($oBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new part to the existing body
|
||||
*
|
||||
* @param $sText
|
||||
* @param string $sMimeType
|
||||
*
|
||||
@@ -468,8 +441,7 @@ class EMail
|
||||
*/
|
||||
public function AddPart($sText, string $sMimeType = Mime::TYPE_HTML)
|
||||
{
|
||||
if (!array_key_exists('parts', $this->m_aData))
|
||||
{
|
||||
if (!array_key_exists('parts', $this->m_aData)) {
|
||||
$this->m_aData['parts'] = array();
|
||||
}
|
||||
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
|
||||
@@ -483,7 +455,7 @@ class EMail
|
||||
{
|
||||
$oBody = $this->m_oMessage->getBody();
|
||||
|
||||
if(!$oBody->isMultiPart()){
|
||||
if (!$oBody->isMultiPart()) {
|
||||
$multipart_content = new Part($oBody->generateMessage());
|
||||
$multipart_content->setType($oBody->getParts()[0]->getType());
|
||||
$multipart_content->setBoundary($oBody->getMime()->boundary());
|
||||
@@ -492,8 +464,7 @@ class EMail
|
||||
$oBody->addPart($multipart_content);
|
||||
}
|
||||
|
||||
if (!array_key_exists('attachments', $this->m_aData))
|
||||
{
|
||||
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);
|
||||
@@ -506,7 +477,7 @@ class EMail
|
||||
|
||||
$oBody->addPart($oNewAttachment);
|
||||
|
||||
if($oBody->isMultiPart()){
|
||||
if ($oBody->isMultiPart()) {
|
||||
$oContentTypeHeader = $this->m_oMessage->getHeaders();
|
||||
foreach ($oContentTypeHeader as $oHeader) {
|
||||
if (!$oHeader instanceof ContentType) {
|
||||
@@ -535,27 +506,25 @@ class EMail
|
||||
|
||||
/**
|
||||
* Helper to transform and sanitize addresses
|
||||
* - get rid of empty addresses
|
||||
*/
|
||||
* - get rid of empty addresses
|
||||
*/
|
||||
protected function AddressStringToArray($sAddressCSVList)
|
||||
{
|
||||
$aAddresses = array();
|
||||
foreach(explode(',', $sAddressCSVList) as $sAddress)
|
||||
{
|
||||
foreach (explode(',', $sAddressCSVList) as $sAddress) {
|
||||
$sAddress = trim($sAddress);
|
||||
if (strlen($sAddress) > 0)
|
||||
{
|
||||
if (strlen($sAddress) > 0) {
|
||||
$aAddresses[] = $sAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAddresses;
|
||||
}
|
||||
|
||||
|
||||
public function SetRecipientTO($sAddress)
|
||||
{
|
||||
$this->m_aData['to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
if (!empty($sAddress)) {
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setTo($aAddresses);
|
||||
}
|
||||
@@ -564,32 +533,25 @@ class EMail
|
||||
public function GetRecipientTO($bAsString = false)
|
||||
{
|
||||
$aRes = $this->m_oMessage->getTo();
|
||||
if ($aRes === null || $aRes->count() === 0)
|
||||
{
|
||||
if ($aRes === null || $aRes->count() === 0) {
|
||||
// There is no "To" header field
|
||||
$aRes = array();
|
||||
}
|
||||
if ($bAsString)
|
||||
{
|
||||
if ($bAsString) {
|
||||
$aStrings = array();
|
||||
foreach ($aRes as $oEmail)
|
||||
{
|
||||
foreach ($aRes as $oEmail) {
|
||||
$sName = $oEmail->getName();
|
||||
$sEmail = $oEmail->getEmail();
|
||||
if (is_null($sName))
|
||||
{
|
||||
if (is_null($sName)) {
|
||||
$aStrings[] = $sEmail;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sName = str_replace(array('<', '>'), '', $sName);
|
||||
$aStrings[] = "$sName <$sEmail>";
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $aStrings);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
@@ -597,8 +559,7 @@ class EMail
|
||||
public function SetRecipientCC($sAddress)
|
||||
{
|
||||
$this->m_aData['cc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
if (!empty($sAddress)) {
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setCc($aAddresses);
|
||||
}
|
||||
@@ -607,8 +568,7 @@ class EMail
|
||||
public function SetRecipientBCC($sAddress)
|
||||
{
|
||||
$this->m_aData['bcc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
if (!empty($sAddress)) {
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setBcc($aAddresses);
|
||||
}
|
||||
@@ -617,12 +577,9 @@ class EMail
|
||||
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))
|
||||
{
|
||||
if ($sLabel != '') {
|
||||
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
|
||||
} else if (!empty($sAddress)) {
|
||||
$this->m_oMessage->setFrom($sAddress);
|
||||
}
|
||||
}
|
||||
@@ -630,12 +587,9 @@ class EMail
|
||||
public function SetRecipientReplyTo($sAddress, $sLabel = '')
|
||||
{
|
||||
$this->m_aData['reply_to'] = array('address' => $sAddress, 'label' => $sLabel);
|
||||
if ($sLabel != '')
|
||||
{
|
||||
if ($sLabel != '') {
|
||||
$this->m_oMessage->setReplyTo(array($sAddress => $sLabel));
|
||||
}
|
||||
else if (!empty($sAddress))
|
||||
{
|
||||
} else if (!empty($sAddress)) {
|
||||
$this->m_oMessage->setReplyTo($sAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,8 @@ use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
|
||||
|
||||
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
||||
|
||||
|
||||
define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
class EmailSwiftMailer
|
||||
{
|
||||
// Serialization formats
|
||||
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
|
||||
|
||||
protected static $m_oConfig = null;
|
||||
protected $m_aData; // For storing data to serialize
|
||||
|
||||
@@ -155,6 +145,11 @@ class EmailSwiftMailer
|
||||
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
|
||||
|
||||
@@ -71,14 +71,14 @@ abstract class Controller
|
||||
* @param string $sViewPath Path of the twig files
|
||||
* @param string $sModuleName name of the module (or 'core' if not a module)
|
||||
*/
|
||||
public function __construct($sViewPath, $sModuleName = 'core')
|
||||
public function __construct($sViewPath, $sModuleName = 'core', $aAdditionalPaths = [])
|
||||
{
|
||||
$this->m_aLinkedScripts = array();
|
||||
$this->m_aLinkedStylesheets = array();
|
||||
$this->m_aSaas = array();
|
||||
$this->m_aAjaxTabs = array();
|
||||
$this->m_aDefaultParams = array();
|
||||
$this->SetViewPath($sViewPath);
|
||||
$this->SetViewPath($sViewPath, $aAdditionalPaths);
|
||||
$this->SetModuleName($sModuleName);
|
||||
if ($sModuleName != 'core')
|
||||
{
|
||||
@@ -116,9 +116,9 @@ abstract class Controller
|
||||
*
|
||||
* @param string $sViewPath
|
||||
*/
|
||||
public function SetViewPath($sViewPath)
|
||||
public function SetViewPath($sViewPath, $aAdditionalPaths = [])
|
||||
{
|
||||
$oTwig = TwigHelper::GetTwigEnvironment($sViewPath);
|
||||
$oTwig = TwigHelper::GetTwigEnvironment($sViewPath, $aAdditionalPaths);
|
||||
$this->m_oTwig = $oTwig;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,13 @@ use WebPage;
|
||||
|
||||
class TwigHelper
|
||||
{
|
||||
public static function GetTwigEnvironment($sViewPath)
|
||||
public static function GetTwigEnvironment($sViewPath, $aAdditionalPaths = [])
|
||||
{
|
||||
$oLoader = new Twig_Loader_Filesystem($sViewPath);
|
||||
foreach ($aAdditionalPaths as $sAdditionalPath) {
|
||||
$oLoader->addPath($sAdditionalPath);
|
||||
}
|
||||
|
||||
$oTwig = new Twig_Environment($oLoader);
|
||||
Extension::RegisterTwigExtensions($oTwig);
|
||||
$sLocalPath = utils::LocalPath($sViewPath);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{# @copyright Copyright (C) 2010-2022 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<fieldset class="'ibo-oauth-wizard--conf--panel ibo-oauth-wizard--result--panel'">
|
||||
<fieldset class="ibo-oauth-wizard--conf--panel ibo-oauth-wizard--result--panel">
|
||||
<legend>{{ 'UI:OAuth:Wizard:ResultConf:Panel:Title'|dict_s }}</legend>
|
||||
<p>{{ 'UI:OAuth:Wizard:ResultConf:Panel:Description'|dict_s }}</p>
|
||||
<pre><code id="ibo-oauth-wizard--conf--result"></code></pre>
|
||||
|
||||
Reference in New Issue
Block a user