Merge branch 'master' of ssh://172.20.0.1/var/git/espo/backend
This commit is contained in:
@@ -64,16 +64,19 @@ class Importer
|
||||
return $this->filtersMatcher;
|
||||
}
|
||||
|
||||
public function importMessage($message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null)
|
||||
public function importMessage($message, $assignedUserId = null, $teamsIdList = [], $userIdList = [], $filterList = [], $fetchOnlyHeader = false, $folderData = null, $parserType = 'Zend')
|
||||
{
|
||||
try {
|
||||
$parserClassName = '\\Espo\\Core\\Mail\\Parsers\\' . $parserType;
|
||||
$parser = new $parserClassName($this->getEntityManager());
|
||||
|
||||
$email = $this->getEntityManager()->getEntity('Email');
|
||||
|
||||
$email->set('isBeingImported', true);
|
||||
|
||||
$subject = '';
|
||||
if (isset($message->subject)) {
|
||||
$subject = $message->subject;
|
||||
if ($parser->checkMessageAttribute($message, 'subject')) {
|
||||
$subject = $parser->getMessageAttribute($message, 'subject');
|
||||
}
|
||||
if (!empty($subject) && is_string($subject)) {
|
||||
$subject = trim($subject);
|
||||
@@ -98,17 +101,18 @@ class Importer
|
||||
}
|
||||
}
|
||||
|
||||
$fromArr = $this->getAddressListFromMessage($message, 'from');
|
||||
if (isset($message->from)) {
|
||||
$email->set('fromString', $message->from);
|
||||
}
|
||||
if (isset($message->replyTo)) {
|
||||
$email->set('replyToString', $message->replyTo);
|
||||
if ($parser->checkMessageAttribute($message, 'from')) {
|
||||
$email->set('fromString', $parser->getMessageAttribute($message, 'from'));
|
||||
}
|
||||
|
||||
$toArr = $this->getAddressListFromMessage($message, 'to');
|
||||
$ccArr = $this->getAddressListFromMessage($message, 'cc');
|
||||
$replyToArr = $this->getAddressListFromMessage($message, 'replyTo');
|
||||
if ($parser->checkMessageAttribute($message, 'replyTo')) {
|
||||
$email->set('replyToString', $parser->getMessageAttribute($message, 'replyTo'));
|
||||
}
|
||||
|
||||
$fromArr = $parser->getAddressListFromMessage($message, 'from');
|
||||
$toArr = $parser->getAddressListFromMessage($message, 'to');
|
||||
$ccArr = $parser->getAddressListFromMessage($message, 'cc');
|
||||
$replyToArr = $parser->getAddressListFromMessage($message, 'replyTo');
|
||||
|
||||
$email->set('from', $fromArr[0]);
|
||||
$email->set('to', implode(';', $toArr));
|
||||
@@ -125,15 +129,14 @@ class Importer
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($message->messageId) && !empty($message->messageId)) {
|
||||
$messageId = $message->messageId;
|
||||
$messageId = str_replace('<<', '<', $messageId);
|
||||
$messageId = str_replace('>>', '>', $messageId);
|
||||
if ($parser->checkMessageAttribute($message, 'messageId') && $parser->getMessageAttribute($message, 'messageId')) {
|
||||
$messageId = $parser->getMessageMessageId($message);
|
||||
|
||||
$email->set('messageId', $messageId);
|
||||
if (isset($message->deliveredTo)) {
|
||||
$email->set('messageIdInternal', $message->messageId . '-' . $message->deliveredTo);
|
||||
if ($parser->checkMessageAttribute($message, 'deliveredTo')) {
|
||||
$email->set('messageIdInternal', $messageId . '-' . $parser->getMessageAttribute($message, 'deliveredTo'));
|
||||
}
|
||||
if (stripos($message->messageId, '@espo-system') !== false) {
|
||||
if (stripos($messageId, '@espo-system') !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -167,49 +170,29 @@ class Importer
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
if (isset($message->date)) {
|
||||
$dt = new \DateTime($message->date);
|
||||
if ($dt) {
|
||||
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('dateSent', $dateSent);
|
||||
}
|
||||
if ($parser->checkMessageAttribute($message, 'date')) {
|
||||
try {
|
||||
$dt = new \DateTime($parser->getMessageAttribute($message, 'date'));
|
||||
if ($dt) {
|
||||
$dateSent = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('dateSent', $dateSent);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
} else {
|
||||
$email->set('dateSent', date('Y-m-d H:i:s'));
|
||||
}
|
||||
if (isset($message->deliveryDate)) {
|
||||
$dt = new \DateTime($message->deliveryDate);
|
||||
if ($dt) {
|
||||
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('deliveryDate', $deliveryDate);
|
||||
}
|
||||
if ($parser->checkMessageAttribute($message, 'deliveryDate')) {
|
||||
try {
|
||||
$dt = new \DateTime($parser->getMessageAttribute($message, 'deliveryDate'));
|
||||
if ($dt) {
|
||||
$deliveryDate = $dt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
|
||||
$email->set('deliveryDate', $deliveryDate);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
$inlineIds = array();
|
||||
|
||||
if (!$fetchOnlyHeader) {
|
||||
if ($message->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($message) as $part) {
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
$this->importPartDataToEmail($email, $message, $inlineIds, 'text/plain');
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
$body = $email->get('body');
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
if (strpos($body, 'cid:' . $cid) !== false) {
|
||||
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&id=' . $attachmentId, $body);
|
||||
} else {
|
||||
$email->addLinkMultipleId('attachments', $attachmentId);
|
||||
}
|
||||
}
|
||||
$email->set('body', $body);
|
||||
}
|
||||
$parser->fetchContentParts($email, $message);
|
||||
|
||||
if ($this->getFiltersMatcher()->match($email, $filterList)) {
|
||||
return false;
|
||||
@@ -222,8 +205,9 @@ class Importer
|
||||
$parentFound = false;
|
||||
|
||||
$replied = null;
|
||||
if (isset($message->inReplyTo) && !empty($message->inReplyTo)) {
|
||||
$arr = explode(' ', $message->inReplyTo);
|
||||
|
||||
if ($parser->checkMessageAttribute($message, 'inReplyTo') && $parser->getMessageAttribute($message, 'inReplyTo')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'inReplyTo'));
|
||||
$inReplyTo = $arr[0];
|
||||
$replied = $this->getEntityManager()->getRepository('Email')->where(array(
|
||||
'messageId' => $inReplyTo
|
||||
@@ -233,14 +217,17 @@ class Importer
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($message->references) && !empty($message->references)) {
|
||||
$arr = explode(' ', $message->references);
|
||||
if ($parser->checkMessageAttribute($message, 'references') && $parser->getMessageAttribute($message, 'references')) {
|
||||
$arr = explode(' ', $parser->getMessageAttribute($message, 'references'));
|
||||
$reference = $arr[0];
|
||||
$reference = str_replace(array('/', '@'), " ", trim($reference, '<>'));
|
||||
$parentType = $parentId = null;
|
||||
$emailSent = PHP_INT_MAX;
|
||||
$number = null;
|
||||
$n = sscanf($reference, '%s %s %d %d espo', $parentType, $parentId, $emailSent, $number);
|
||||
if ($n != 4) {
|
||||
$n = sscanf($reference, '%s %s %d %d espo-system', $parentType, $parentId, $emailSent, $number);
|
||||
}
|
||||
if ($n == 4 && $emailSent < time()) {
|
||||
if (!empty($parentType) && !empty($parentId)) {
|
||||
if ($parentType == 'Lead') {
|
||||
@@ -354,243 +341,4 @@ class Importer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function normilizeHeader($header)
|
||||
{
|
||||
if (is_a($header, 'ArrayIterator')) {
|
||||
return $header->current();
|
||||
} else {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$addressList = array();
|
||||
if (isset($message->$type)) {
|
||||
$list = $this->normilizeHeader($message->getHeader($type))->getAddressList();
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address->getEmail();
|
||||
}
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
|
||||
{
|
||||
try {
|
||||
$type = null;
|
||||
|
||||
if ($part->getHeaders() && isset($part->contentType)) {
|
||||
$type = strtok($part->contentType, ';');
|
||||
}
|
||||
|
||||
$contentDisposition = false;
|
||||
if (isset($part->ContentDisposition)) {
|
||||
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
|
||||
$contentDisposition = 'attachment';
|
||||
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
} else if (isset($part->contentID)) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
if (!empty($defaultContentType)) {
|
||||
$type = $defaultContentType;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$encoding = null;
|
||||
$isAttachment = true;
|
||||
if ($type == 'text/plain' || $type == 'text/html') {
|
||||
if ($contentDisposition !== 'attachment') {
|
||||
$isAttachment = false;
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
$bodyPlain = '';
|
||||
if ($email->get('bodyPlain')) {
|
||||
$bodyPlain .= $email->get('bodyPlain') . "\n";
|
||||
}
|
||||
$bodyPlain .= $content;
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
} else if ($type == 'text/html') {
|
||||
$body = '';
|
||||
if ($email->get('body')) {
|
||||
$body .= $email->get('body') . "<br>";
|
||||
}
|
||||
$body .= $content;
|
||||
$email->set('isHtml', true);
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAttachment) {
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
if ($contentDisposition) {
|
||||
if ($contentDisposition === 'attachment') {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
} else if ($contentDisposition === 'inline') {
|
||||
if (isset($part->contentID)) {
|
||||
$contentId = trim($part->contentID, '<>');
|
||||
$fileName = $contentId;
|
||||
$disposition = 'inline';
|
||||
} else {
|
||||
// for iOS attachments
|
||||
if (empty($fileName)) {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$attachment->set('contents', $content);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function decodeAttachmentFileName($fileName)
|
||||
{
|
||||
if ($fileName && stripos($fileName, "''") !== false) {
|
||||
list($encoding, $fileName) = explode("''", $fileName);
|
||||
$fileName = rawurldecode($fileName);
|
||||
if (strtoupper($encoding) !== 'UTF-8') {
|
||||
if ($encoding) {
|
||||
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function fetchFileNameFromContentDisposition($contentDisposition)
|
||||
{
|
||||
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
|
||||
|
||||
$fileName = false;
|
||||
$m = array();
|
||||
|
||||
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else {
|
||||
$fileName = '';
|
||||
foreach (['0', '1'] as $i) {
|
||||
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName === '') {
|
||||
$fileName = null;
|
||||
} else {
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function getContentFromPart($part)
|
||||
{
|
||||
if ($part instanceof \Zend\Mime\Part) {
|
||||
$content = $part->getRawContent();
|
||||
if (strtolower($part->charset) != 'utf-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
|
||||
}
|
||||
} else {
|
||||
$content = $part->getContent();
|
||||
|
||||
$encoding = null;
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
$encoding = strtolower($cteHeader->getTransferEncoding());
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
if (isset($part->contentType)) {
|
||||
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
|
||||
$charsetParamValue = $ctHeader->getParameter('charset');
|
||||
if (!empty($charsetParamValue)) {
|
||||
$charset = strtoupper($charsetParamValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
|
||||
$content = quoted_printable_decode($content);
|
||||
}
|
||||
}
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Parsers;
|
||||
|
||||
class Zend
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct($entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->entityManager;
|
||||
}
|
||||
|
||||
public function checkMessageAttribute($message, $attribute)
|
||||
{
|
||||
return isset($message->$attribute);
|
||||
}
|
||||
|
||||
public function getMessageAttribute($message, $attribute)
|
||||
{
|
||||
if (!isset($message->$attribute)) return null;
|
||||
|
||||
return $message->$attribute;
|
||||
}
|
||||
|
||||
public function getMessageMessageId($message)
|
||||
{
|
||||
if (!isset($message->messageId)) return null;
|
||||
|
||||
$messageId = $message->messageId;
|
||||
$messageId = str_replace('<<', '<', $messageId);
|
||||
$messageId = str_replace('>>', '>', $messageId);
|
||||
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
public function getAddressListFromMessage($message, $type)
|
||||
{
|
||||
$addressList = array();
|
||||
if (isset($message->$type)) {
|
||||
$list = $this->normilizeHeader($message->getHeader($type))->getAddressList();
|
||||
foreach ($list as $address) {
|
||||
$addressList[] = $address->getEmail();
|
||||
}
|
||||
}
|
||||
return $addressList;
|
||||
}
|
||||
|
||||
public function fetchContentParts(\Espo\Entities\Email $email, $message)
|
||||
{
|
||||
$inlineIds = array();
|
||||
|
||||
if ($message->isMultipart()) {
|
||||
foreach (new \RecursiveIteratorIterator($message) as $part) {
|
||||
$this->importPartDataToEmail($email, $part, $inlineIds);
|
||||
}
|
||||
} else {
|
||||
$this->importPartDataToEmail($email, $message, $inlineIds, 'text/plain');
|
||||
}
|
||||
|
||||
if (!$email->get('body') && $email->get('bodyPlain')) {
|
||||
$email->set('body', $email->get('bodyPlain'));
|
||||
}
|
||||
|
||||
$body = $email->get('body');
|
||||
if (!empty($body)) {
|
||||
foreach ($inlineIds as $cid => $attachmentId) {
|
||||
if (strpos($body, 'cid:' . $cid) !== false) {
|
||||
$body = str_replace('cid:' . $cid, '?entryPoint=attachment&id=' . $attachmentId, $body);
|
||||
} else {
|
||||
$email->addLinkMultipleId('attachments', $attachmentId);
|
||||
}
|
||||
}
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
|
||||
protected function importPartDataToEmail(\Espo\Entities\Email $email, $part, &$inlineIds = array(), $defaultContentType = null)
|
||||
{
|
||||
try {
|
||||
$type = null;
|
||||
|
||||
if ($part->getHeaders() && isset($part->contentType)) {
|
||||
$type = strtok($part->contentType, ';');
|
||||
}
|
||||
|
||||
$contentDisposition = false;
|
||||
if (isset($part->ContentDisposition)) {
|
||||
if (strpos(strtolower($part->ContentDisposition), 'attachment') === 0) {
|
||||
$contentDisposition = 'attachment';
|
||||
} else if (strpos(strtolower($part->ContentDisposition), 'inline') === 0) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
} else if (isset($part->contentID)) {
|
||||
$contentDisposition = 'inline';
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
if (!empty($defaultContentType)) {
|
||||
$type = $defaultContentType;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$encoding = null;
|
||||
$isAttachment = true;
|
||||
if ($type == 'text/plain' || $type == 'text/html') {
|
||||
if ($contentDisposition !== 'attachment') {
|
||||
$isAttachment = false;
|
||||
$content = $this->getContentFromPart($part);
|
||||
if ($type == 'text/plain') {
|
||||
$bodyPlain = '';
|
||||
if ($email->get('bodyPlain')) {
|
||||
$bodyPlain .= $email->get('bodyPlain') . "\n";
|
||||
}
|
||||
$bodyPlain .= $content;
|
||||
$email->set('bodyPlain', $bodyPlain);
|
||||
} else if ($type == 'text/html') {
|
||||
$body = '';
|
||||
if ($email->get('body')) {
|
||||
$body .= $email->get('body') . "<br>";
|
||||
}
|
||||
$body .= $content;
|
||||
$email->set('isHtml', true);
|
||||
$email->set('body', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAttachment) {
|
||||
$content = $part->getContent();
|
||||
$disposition = null;
|
||||
|
||||
$fileName = null;
|
||||
$contentId = null;
|
||||
|
||||
if ($contentDisposition) {
|
||||
if ($contentDisposition === 'attachment') {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
} else if ($contentDisposition === 'inline') {
|
||||
if (isset($part->contentID)) {
|
||||
$contentId = trim($part->contentID, '<>');
|
||||
$fileName = $contentId;
|
||||
$disposition = 'inline';
|
||||
} else {
|
||||
// for iOS attachments
|
||||
if (empty($fileName)) {
|
||||
$fileName = $this->fetchFileNameFromContentDisposition($part->ContentDisposition);
|
||||
if ($fileName) {
|
||||
$disposition = 'attachment';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$encoding = strtolower($this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'))->getTransferEncoding());
|
||||
}
|
||||
|
||||
$attachment = $this->getEntityManager()->getEntity('Attachment');
|
||||
$attachment->set('name', $fileName);
|
||||
$attachment->set('type', $type);
|
||||
|
||||
if ($disposition == 'inline') {
|
||||
$attachment->set('role', 'Inline Attachment');
|
||||
} else {
|
||||
$attachment->set('role', 'Attachment');
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$attachment->set('contents', $content);
|
||||
|
||||
$this->getEntityManager()->saveEntity($attachment);
|
||||
|
||||
if ($disposition == 'attachment') {
|
||||
$attachmentsIds = $email->get('attachmentsIds');
|
||||
$attachmentsIds[] = $attachment->id;
|
||||
$email->set('attachmentsIds', $attachmentsIds);
|
||||
} else if ($disposition == 'inline') {
|
||||
$inlineIds[$contentId] = $attachment->id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
protected function getContentFromPart($part)
|
||||
{
|
||||
if ($part instanceof \Zend\Mime\Part) {
|
||||
$content = $part->getRawContent();
|
||||
if (strtolower($part->charset) != 'utf-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $part->charset);
|
||||
}
|
||||
} else {
|
||||
$content = $part->getContent();
|
||||
|
||||
$encoding = null;
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
$encoding = strtolower($cteHeader->getTransferEncoding());
|
||||
}
|
||||
|
||||
if ($encoding == 'base64') {
|
||||
$content = base64_decode($content);
|
||||
}
|
||||
|
||||
$charset = 'UTF-8';
|
||||
|
||||
if (isset($part->contentType)) {
|
||||
$ctHeader = $this->normilizeHeader($part->getHeader('Content-Type'));
|
||||
$charsetParamValue = $ctHeader->getParameter('charset');
|
||||
if (!empty($charsetParamValue)) {
|
||||
$charset = strtoupper($charsetParamValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($part->contentTransferEncoding)) {
|
||||
$cteHeader = $this->normilizeHeader($part->getHeader('Content-Transfer-Encoding'));
|
||||
if ($cteHeader->getTransferEncoding() == 'quoted-printable') {
|
||||
$content = quoted_printable_decode($content);
|
||||
}
|
||||
}
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$content = mb_convert_encoding($content, 'UTF-8', $charset);
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function normilizeHeader($header)
|
||||
{
|
||||
if (is_a($header, 'ArrayIterator')) {
|
||||
return $header->current();
|
||||
} else {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
|
||||
protected function fetchFileNameFromContentDisposition($contentDisposition)
|
||||
{
|
||||
$contentDisposition = preg_replace('/\\\\"/', "{{_!Q!U!O!T!E!_}}", $contentDisposition);
|
||||
|
||||
$fileName = false;
|
||||
$m = array();
|
||||
|
||||
if (preg_match('/filename="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
} else if (preg_match('/filename\*="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else if (preg_match('/filename\*=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$fileName = $m[1];
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
} else {
|
||||
$fileName = '';
|
||||
foreach (['0', '1'] as $i) {
|
||||
if (preg_match('/filename\*'.$i.'[\*]?="([^"]+)";?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
} else if (preg_match('/filename\*'.$i.'[\*]?=([^";]+);?/i', $contentDisposition, $m)) {
|
||||
$part = $m[1];
|
||||
$fileName .= $part;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName === '') {
|
||||
$fileName = null;
|
||||
} else {
|
||||
$fileName = $this->decodeAttachmentFileName($fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
$fileName = str_replace('{{_!Q!U!O!T!E!_}}', '"', $fileName);
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
protected function decodeAttachmentFileName($fileName)
|
||||
{
|
||||
if ($fileName && stripos($fileName, "''") !== false) {
|
||||
list($encoding, $fileName) = explode("''", $fileName);
|
||||
$fileName = rawurldecode($fileName);
|
||||
if (strtoupper($encoding) !== 'UTF-8') {
|
||||
if ($encoding) {
|
||||
$fileName = mb_convert_encoding($fileName, 'UTF-8', $encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,14 +34,11 @@ use \Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
class Lead extends \Espo\Core\Controllers\Record
|
||||
{
|
||||
public function actionConvert($params, $data, $request)
|
||||
public function postActionConvert($params, $data, $request)
|
||||
{
|
||||
if (empty($data['id'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
if (!$request->isPost()) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
$entity = $this->getRecordService()->convert($data['id'], $data['records']);
|
||||
|
||||
if (!empty($entity)) {
|
||||
@@ -49,4 +46,13 @@ class Lead extends \Espo\Core\Controllers\Record
|
||||
}
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public function postActionGetConvertAttributes($params, $data, $request)
|
||||
{
|
||||
if (empty($data['id'])) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getRecordService()->getConvertAttributes($data['id']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class CampaignTrackOpened extends \Espo\Core\EntryPoints\Base
|
||||
imagefill($img, 0, 0, $color);
|
||||
|
||||
imagepng($img);
|
||||
imagecolordeallocate($background);
|
||||
imagecolordeallocate($color);
|
||||
imagedestroy($img);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Modules\Crm\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
class Meeting extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
@@ -210,7 +211,7 @@ class Meeting extends \Espo\Core\ORM\Repositories\RDB
|
||||
$remindAt->sub(new \DateInterval('PT' . $seconds . 'S'));
|
||||
|
||||
foreach ($userIdList as $userId) {
|
||||
$id = uniqid(true);
|
||||
$id = Util::generateId();
|
||||
|
||||
$sql = "
|
||||
INSERT
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"Insurance": "Insurance",
|
||||
"Legal": "Legal",
|
||||
"Manufacturing": "Manufacturing",
|
||||
"Marketing": "Marketing",
|
||||
"Publishing": "Publishing",
|
||||
"Real Estate": "Real Estate",
|
||||
"Service": "Service",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"Insurance",
|
||||
"Legal",
|
||||
"Manufacturing",
|
||||
"Marketing",
|
||||
"Publishing",
|
||||
"Real Estate",
|
||||
"Service",
|
||||
|
||||
@@ -1101,22 +1101,39 @@ class Activities extends \Espo\Core\Services\Base
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
$rowList = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$result = array();
|
||||
foreach ($rows as $row) {
|
||||
$entity = $this->getEntityManager()->getEntity($row['entityType'], $row['entityId']);
|
||||
foreach ($rowList as $row) {
|
||||
$reminderId = $row['id'];
|
||||
$entityType = $row['entityType'];
|
||||
$entityId = $row['entityId'];
|
||||
|
||||
|
||||
$entity = $this->getEntityManager()->getEntity($entityType, $entityId);
|
||||
$data = null;
|
||||
|
||||
if ($entity) {
|
||||
if ($entityType === 'Meeting' || $entityType === 'Call') {
|
||||
$entity->loadLinkMultipleField('users', array('status' => 'acceptanceStatus'));
|
||||
$status = $entity->getLinkMultipleColumn('users', 'status', $userId);
|
||||
if ($status === 'Declined') {
|
||||
$this->removeReminder($reminderId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'id' => $entity->id,
|
||||
'entityType' => $row['entityType'],
|
||||
'entityType' => $entityType,
|
||||
'dateStart' => $entity->get('dateStart'),
|
||||
'name' => $entity->get('name')
|
||||
);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$result[] = array(
|
||||
'id' => $row['id'],
|
||||
'id' => $reminderId,
|
||||
'data' => $data
|
||||
);
|
||||
|
||||
|
||||
@@ -36,6 +36,18 @@ use \Espo\ORM\Entity;
|
||||
|
||||
class Lead extends \Espo\Services\Record
|
||||
{
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->addDependency('container');
|
||||
}
|
||||
|
||||
protected function getFieldManager()
|
||||
{
|
||||
return $this->getInjection('container')->get('fieldManager');
|
||||
}
|
||||
|
||||
protected function getDuplicateWhereClause(Entity $entity, $data = array())
|
||||
{
|
||||
$data = array(
|
||||
@@ -100,6 +112,105 @@ class Lead extends \Espo\Services\Record
|
||||
}
|
||||
}
|
||||
|
||||
public function getConvertAttributes($id)
|
||||
{
|
||||
$lead = $this->getEntity($id);
|
||||
|
||||
if (!$this->getAcl()->check($lead, 'read')) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
$entityList = $this->getMetadata()->get('entityDefs.Lead.convertEntityList', []);
|
||||
|
||||
$ignoreAttributeList = ['createdAt', 'modifiedAt', 'modifiedById', 'modifiedByName', 'createdById', 'createdByName'];
|
||||
|
||||
$convertFieldsDefs = $this->getMetadata()->get('entityDefs.Lead.convertFields', array());
|
||||
|
||||
foreach ($entityList as $entityType) {
|
||||
if (!$this->getAcl()->checkScope($entityType, 'edit')) continue;
|
||||
|
||||
$attributes = array();
|
||||
|
||||
$target = $this->getEntityManager()->getEntity($entityType);
|
||||
|
||||
$fieldMap = array();
|
||||
|
||||
$fieldList = array_keys($this->getMetadata()->get('entityDefs.Lead.fields', array()));
|
||||
foreach ($fieldList as $field) {
|
||||
if (!$this->getMetadata()->get('entityDefs.'.$entityType.'.fields.' . $field)) continue;
|
||||
if (
|
||||
$this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type'])
|
||||
!==
|
||||
$this->getMetadata()->get(['entityDefs', 'Lead', 'fields', $field, 'type'])
|
||||
) continue;
|
||||
|
||||
$fieldMap[$field] = $field;
|
||||
}
|
||||
if (array_key_exists($entityType, $convertFieldsDefs)) {
|
||||
foreach ($convertFieldsDefs[$entityType] as $field => $leadField) {
|
||||
$fieldMap[$field] = $leadField;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fieldMap as $field => $leadField) {
|
||||
$type = $this->getMetadata()->get(['entityDefs', 'Lead', 'fields', $field, 'type']);
|
||||
|
||||
|
||||
if (in_array($type, ['file', 'image'])) {
|
||||
$attachment = $lead->get($field);
|
||||
if ($attachment) {
|
||||
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
|
||||
$idAttribute = $field . 'Id';
|
||||
$nameAttribute = $field . 'Name';
|
||||
if ($attachment) {
|
||||
$attributes[$idAttribute] = $attachment->id;
|
||||
$attributes[$nameAttribute] = $attachment->get('name');
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (in_array($type, ['attachmentMultiple'])) {
|
||||
$attachmentList = $lead->get($field);
|
||||
if (count($attachmentList)) {
|
||||
$idList = [];
|
||||
$nameHash = (object) [];
|
||||
$typeHash = (object) [];
|
||||
foreach ($attachmentList as $attachment) {
|
||||
$attachment = $this->getEntityManager()->getRepository('Attachment')->getCopiedAttachment($attachment);
|
||||
if ($attachment) {
|
||||
$idList[] = $attachment->id;
|
||||
$nameHash->{$attachment->id} = $attachment->get('name');
|
||||
$typeHash->{$attachment->id} = $attachment->get('type');
|
||||
}
|
||||
}
|
||||
$attributes[$field . 'Ids'] = $idList;
|
||||
$attributes[$field . 'Names'] = $nameHash;
|
||||
$attributes[$field . 'Types'] = $typeHash;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$leadAttributeList = $this->getFieldManager()->getAttributeList('Lead', $leadField);
|
||||
$attributeList = $this->getFieldManager()->getAttributeList($entityType, $field);
|
||||
|
||||
foreach ($attributeList as $i => $attribute) {
|
||||
if (in_array($attribute, $ignoreAttributeList)) continue;
|
||||
|
||||
$leadAttribute = $leadAttributeList[$i];
|
||||
if (!$lead->has($leadAttribute)) continue;
|
||||
|
||||
$attributes[$attribute] = $lead->get($leadAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
$data[$entityType] = $attributes;
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function convert($id, $recordsData)
|
||||
{
|
||||
$lead = $this->getEntity($id);
|
||||
|
||||
@@ -43,10 +43,13 @@ class EntityFactory
|
||||
public function create($name)
|
||||
{
|
||||
$className = $this->entityManager->normalizeEntityName($name);
|
||||
$defs = $this->metadata->get($name);
|
||||
if (!class_exists($className)) {
|
||||
return null;
|
||||
}
|
||||
$defs = $this->metadata->get($name);
|
||||
if (is_null($defs)) {
|
||||
return null;
|
||||
}
|
||||
$entity = new $className($defs, $this->entityManager);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
@@ -38,11 +38,11 @@ class Metadata
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function get($entityName)
|
||||
public function get($entityType)
|
||||
{
|
||||
return $this->data[$entityName];
|
||||
if (!array_key_exists($entityType, $this->data)) {
|
||||
return null;
|
||||
}
|
||||
return $this->data[$entityType];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -104,16 +104,17 @@ class RDB extends \Espo\ORM\Repository
|
||||
|
||||
protected function getEntityById($id)
|
||||
{
|
||||
$entity = $this->entityFactory->create($this->entityType);
|
||||
|
||||
if (!$entity) return null;
|
||||
|
||||
$params = array();
|
||||
$this->handleSelectParams($params);
|
||||
|
||||
$entity = $this->entityFactory->create($this->entityType);
|
||||
if ($entity) {
|
||||
if ($this->getMapper()->selectById($entity, $id, $params)) {
|
||||
$entity->setAsFetched();
|
||||
return $entity;
|
||||
}
|
||||
if ($this->getMapper()->selectById($entity, $id, $params)) {
|
||||
$entity->setAsFetched();
|
||||
return $entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,9 @@ class Email extends \Espo\Core\ORM\Repositories\RDB
|
||||
$idHash = (object) [];
|
||||
foreach ($addressList as $address) {
|
||||
$p = $this->getEntityManager()->getRepository('EmailAddress')->getEntityByAddress($address);
|
||||
if (!$p) {
|
||||
$p = $this->getEntityManager()->getRepository('InboundEmail')->where(array('emailAddress' => $address))->findOne();
|
||||
}
|
||||
if ($p) {
|
||||
$nameHash->$address = $p->get('name');
|
||||
$typeHash->$address = $p->getEntityName();
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"fieldLevel": {
|
||||
},
|
||||
"assignmentPermission": "all",
|
||||
"userPermission": "no",
|
||||
"userPermission": "all",
|
||||
"portalPermission": "no"
|
||||
},
|
||||
"scopeLevelTypesDefaults": {
|
||||
|
||||
@@ -119,10 +119,24 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
}
|
||||
$d = array(
|
||||
'usersMiddle.inTrash=' => false,
|
||||
'usersMiddle.folderId' => null
|
||||
'usersMiddle.folderId' => null,
|
||||
array(
|
||||
'status' => ['Archived', 'Sent']
|
||||
)
|
||||
);
|
||||
if (!empty($idList)) {
|
||||
$d['fromEmailAddressId!='] = $idList;
|
||||
$d[] = array(
|
||||
'OR' => array(
|
||||
'status' => 'Archived',
|
||||
'createdById!=' => $this->getUser()->id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$d[] = array(
|
||||
'status' => 'Archived',
|
||||
'createdById!=' => $this->getUser()->id
|
||||
);
|
||||
}
|
||||
$result['whereClause'][] = $d;
|
||||
|
||||
@@ -151,6 +165,9 @@ class Email extends \Espo\Core\SelectManagers\Base
|
||||
'createdById' => $this->getUser()->id
|
||||
)
|
||||
),
|
||||
array(
|
||||
'status!=' => 'Draft'
|
||||
),
|
||||
'usersMiddle.inTrash=' => false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ class Email extends Record
|
||||
$searchByEmailAddress = false;
|
||||
if (!empty($params['where']) && is_array($params['where'])) {
|
||||
foreach ($params['where'] as $i => $p) {
|
||||
if (!empty($p['field']) && $p['field'] == 'emailAddress') {
|
||||
if (!empty($p['attribute']) && $p['attribute'] == 'emailAddress') {
|
||||
$searchByEmailAddress = true;
|
||||
$emailAddress = $p['value'];
|
||||
unset($params['where'][$i]);
|
||||
|
||||
@@ -177,13 +177,13 @@ class EmailAddress extends Record
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$this->findInAddressBookUsers($query, $limit, $result);
|
||||
if ($this->getAcl()->checkScope('Contact')) {
|
||||
$this->findInAddressBookByEntityType($query, $limit, 'Contact', $result);
|
||||
}
|
||||
if ($this->getAcl()->checkScope('Lead')) {
|
||||
$this->findInAddressBookByEntityType($query, $limit, 'Lead', $result);
|
||||
}
|
||||
$this->findInAddressBookUsers($query, $limit, $result);
|
||||
if ($this->getAcl()->checkScope('Account')) {
|
||||
$this->findInAddressBookByEntityType($query, $limit, 'Account', $result);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,9 @@ class EmailNotification extends \Espo\Core\Services\Base
|
||||
'body' => $body,
|
||||
'isHtml' => true,
|
||||
'to' => $emailAddress,
|
||||
'isSystem' => true
|
||||
'isSystem' => true,
|
||||
'parentId' => $entity->id,
|
||||
'parentType' => $entity->getEntityType()
|
||||
));
|
||||
try {
|
||||
$this->getMailSender()->send($email);
|
||||
@@ -429,6 +431,13 @@ class EmailNotification extends \Espo\Core\Services\Base
|
||||
'to' => $emailAddress,
|
||||
'isSystem' => true
|
||||
));
|
||||
if ($parentId && $parentType) {
|
||||
$email->set(array(
|
||||
'parentId' => $parentId,
|
||||
'parentType' => $parentType
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->getMailSender()->send($email);
|
||||
} catch (\Exception $e) {
|
||||
@@ -517,6 +526,12 @@ class EmailNotification extends \Espo\Core\Services\Base
|
||||
'to' => $emailAddress,
|
||||
'isSystem' => true
|
||||
));
|
||||
if ($parentId && $parentType) {
|
||||
$email->set(array(
|
||||
'parentId' => $parentId,
|
||||
'parentType' => $parentType
|
||||
));
|
||||
}
|
||||
try {
|
||||
$this->getMailSender()->send($email);
|
||||
} catch (\Exception $e) {
|
||||
@@ -576,8 +591,11 @@ class EmailNotification extends \Espo\Core\Services\Base
|
||||
'body' => $body,
|
||||
'isHtml' => true,
|
||||
'to' => $emailAddress,
|
||||
'isSystem' => true
|
||||
'isSystem' => true,
|
||||
'parentId' => $parentId,
|
||||
'parentType' => $parentType
|
||||
));
|
||||
|
||||
try {
|
||||
$this->getMailSender()->send($email);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -417,6 +417,9 @@ class Import extends \Espo\Services\Record
|
||||
|
||||
foreach ($importFieldList as $i => $field) {
|
||||
if (!empty($field)) {
|
||||
if (!array_key_exists($i, $row)) {
|
||||
continue;
|
||||
}
|
||||
$value = $row[$i];
|
||||
if ($field == 'id') {
|
||||
if ($params['action'] == 'create') {
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Services;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
@@ -153,7 +154,7 @@ class User extends Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$requestId = uniqid();
|
||||
$requestId = Util::generateId();
|
||||
|
||||
$passwordChangeRequest = $this->getEntityManager()->getEntity('PasswordChangeRequest');
|
||||
$passwordChangeRequest->set(array(
|
||||
|
||||
@@ -38,7 +38,7 @@ Espo.define('crm:views/dashlets/calendar', 'views/dashlets/abstract/base', funct
|
||||
|
||||
init: function () {
|
||||
Dep.prototype.init.call(this);
|
||||
this.optionsFields['enabledScopeList'].options = this.getMetadata().get('clientDefs.Calendar.scopeList') || this.optionsFields['enabledScopeList'].options;
|
||||
this.optionsFields['enabledScopeList'].options = this.getConfig().get('calendarEntityList') || [];
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('crm:views/lead/convert', 'View', function (Dep) {
|
||||
Espo.define('crm:views/lead/convert', 'view', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -90,42 +90,34 @@ Espo.define('crm:views/lead/convert', 'View', function (Dep) {
|
||||
var attributeList = this.getFieldManager().getEntityAttributes(this.model.name);
|
||||
var ignoreAttributeList = ['createdAt', 'modifiedAt', 'modifiedById', 'modifiedByName', 'createdById', 'createdByName'];
|
||||
|
||||
scopeList.forEach(function (scope) {
|
||||
this.getModelFactory().create(scope, function (model) {
|
||||
model.populateDefaults();
|
||||
if (scopeList.length) {
|
||||
this.ajaxPostRequest('Lead/action/getConvertAttributes', {
|
||||
id: this.model.id
|
||||
}).done(function (data) {
|
||||
scopeList.forEach(function (scope) {
|
||||
this.getModelFactory().create(scope, function (model) {
|
||||
model.populateDefaults();
|
||||
|
||||
this.getFieldManager().getEntityAttributes(model.name).forEach(function (attr) {
|
||||
if (~attributeList.indexOf(attr) && !~ignoreAttributeList.indexOf(attr)) {
|
||||
model.set(attr, this.model.get(attr), {silent: true});
|
||||
}
|
||||
model.set(data[scope] || {}, {silent: true});
|
||||
|
||||
this.createView(scope, 'views/record/edit', {
|
||||
model: model,
|
||||
el: '#main .edit-container-' + Espo.Utils.toDom(scope),
|
||||
buttonsPosition: false,
|
||||
layoutName: 'detailConvert',
|
||||
exit: function () {},
|
||||
}, function (view) {
|
||||
i++;
|
||||
if (i == scopeList.length) {
|
||||
this.wait(false);
|
||||
this.notify(false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
for (var field in this.model.defs.convertFields[scope]) {
|
||||
var leadField = this.model.defs.convertFields[scope][field];
|
||||
var leadAttrs = this.getFieldManager().getAttributes(this.model.getFieldParam(leadField, 'type'), leadField);
|
||||
var attrs = this.getFieldManager().getAttributes(model.getFieldParam(field, 'type'), field);
|
||||
|
||||
attrs.forEach(function (attr, i) {
|
||||
var leadAttr = leadAttrs[i];
|
||||
model.set(attr, this.model.get(leadAttr));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.createView(scope, 'Record.Edit', {
|
||||
model: model,
|
||||
el: '#main .edit-container-' + Espo.Utils.toDom(scope),
|
||||
buttonsPosition: false,
|
||||
layoutName: 'detailConvert',
|
||||
exit: function () {},
|
||||
}, function (view) {
|
||||
i++;
|
||||
if (i == scopeList.length) {
|
||||
this.wait(false);
|
||||
this.notify(false);
|
||||
}
|
||||
}.bind(this));
|
||||
}, this);
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if (scopeList.length == 0) {
|
||||
this.wait(false);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
<input type="text" class="main-element form-control input-sm" name="{{name}}" value="{{searchParams.valueText}}" {{#if params.maxLength}} maxlength="{{params.maxLength}}"{{/if}}>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
<input type="text" class="main-element form-control input-sm" name="{{name}}" value="{{searchParams.valueText}}" {{#if params.maxLength}} maxlength="{{params.maxLength}}"{{/if}}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
<select class="form-control search-type input-sm" name="{{name}}-type">
|
||||
{{options searchParams.typeOptions searchParams.typeFront field='varcharSearchRanges'}}
|
||||
{{options searchTypeList searchType field='varcharSearchRanges'}}
|
||||
</select>
|
||||
<input type="text" class="main-element form-control input-sm" name="{{name}}" value="{{searchParams.value}}" {{#if params.maxLength}} maxlength="{{params.maxLength}}"{{/if}}{{#if params.size}} size="{{params.size}}"{{/if}} autocomplete="off" placeholder="{{translate 'Value'}}">
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
<select class="form-control search-type input-sm" name="{{name}}-type">
|
||||
{{options searchParams.typeOptions searchParams.typeFront field='varcharSearchRanges'}}
|
||||
{{options searchTypeList searchType field='varcharSearchRanges'}}
|
||||
</select>
|
||||
<input type="text" class="main-element form-control input-sm" name="{{name}}" value="{{searchParams.value}}" {{#if params.maxLength}} maxlength="{{params.maxLength}}"{{/if}}{{#if params.size}} size="{{params.size}}"{{/if}} autocomplete="off" placeholder="{{translate 'Value'}}">
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/email', 'views/fields/base', function (Dep) {
|
||||
Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -38,11 +38,8 @@ Espo.define('views/fields/email', 'views/fields/base', function (Dep) {
|
||||
|
||||
listTemplate: 'fields/email/list',
|
||||
|
||||
searchTemplate: 'fields/email/search',
|
||||
|
||||
validations: ['required', 'emailData'],
|
||||
|
||||
|
||||
validateEmailData: function () {
|
||||
var data = this.model.get(this.dataFieldName);
|
||||
if (data && data.length) {
|
||||
@@ -370,23 +367,8 @@ Espo.define('views/fields/email', 'views/fields/base', function (Dep) {
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.$element.val() || null;
|
||||
if (value) {
|
||||
if (typeof value.trim === 'function') {
|
||||
value = value.trim();
|
||||
}
|
||||
var data = {
|
||||
type: 'like',
|
||||
value: value + '%',
|
||||
valueText: value
|
||||
};
|
||||
return data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
Espo.define('views/fields/phone', 'views/fields/base', function (Dep) {
|
||||
Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
@@ -38,8 +38,6 @@ Espo.define('views/fields/phone', 'views/fields/base', function (Dep) {
|
||||
|
||||
listTemplate: 'fields/phone/list',
|
||||
|
||||
searchTemplate: 'fields/phone/search',
|
||||
|
||||
validations: ['required'],
|
||||
|
||||
validateRequired: function () {
|
||||
@@ -263,20 +261,8 @@ Espo.define('views/fields/phone', 'views/fields/base', function (Dep) {
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
}
|
||||
|
||||
fetchSearch: function () {
|
||||
var value = this.$element.val().trim() || null;
|
||||
if (value) {
|
||||
var data = {
|
||||
type: 'like',
|
||||
value: value + '%',
|
||||
valueText: value
|
||||
};
|
||||
return data;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -48,6 +48,8 @@ Espo.define('views/fields/text', 'views/fields/base', function (Dep) {
|
||||
|
||||
rowsDefault: 4,
|
||||
|
||||
searchTypeList: ['startsWith', 'contains', 'equals', 'isEmpty', 'isNotEmpty'],
|
||||
|
||||
events: {
|
||||
'click a[data-action="seeMoreText"]': function (e) {
|
||||
this.seeMoreText = true;
|
||||
@@ -62,7 +64,6 @@ Espo.define('views/fields/text', 'views/fields/base', function (Dep) {
|
||||
},
|
||||
|
||||
setupSearch: function () {
|
||||
this.searchParams.typeOptions = ['startsWith', 'contains', 'equals', 'isEmpty', 'isNotEmpty'];
|
||||
this.events = _.extend({
|
||||
'change select.search-type': function (e) {
|
||||
var type = $(e.currentTarget).val();
|
||||
@@ -192,6 +193,10 @@ Espo.define('views/fields/text', 'views/fields/base', function (Dep) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getSearchType: function () {
|
||||
return this.searchParams.typeFront || this.searchParams.type;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -34,8 +34,9 @@ Espo.define('views/fields/varchar', 'views/fields/base', function (Dep) {
|
||||
|
||||
searchTemplate: 'fields/varchar/search',
|
||||
|
||||
searchTypeList: ['startsWith', 'endsWith', 'contains', 'equals', 'like', 'isEmpty', 'isNotEmpty'],
|
||||
|
||||
setupSearch: function () {
|
||||
this.searchParams.typeOptions = ['startsWith', 'endsWith', 'contains', 'equals', 'like', 'isEmpty', 'isNotEmpty'];
|
||||
this.events = _.extend({
|
||||
'change select.search-type': function (e) {
|
||||
var type = $(e.currentTarget).val();
|
||||
@@ -131,6 +132,10 @@ Espo.define('views/fields/varchar', 'views/fields/base', function (Dep) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getSearchType: function () {
|
||||
return this.searchParams.typeFront || this.searchParams.type;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2016 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
|
||||
* Website: http://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace tests\integration\Espo\Email;
|
||||
|
||||
class SearchByEmailAddressTest extends \tests\integration\Core\BaseTestCase
|
||||
{
|
||||
|
||||
public function testSearchByEmailAddress()
|
||||
{
|
||||
$entityManager = $this->getContainer()->get('entityManager');
|
||||
|
||||
$email = $entityManager->getEntity('Email');
|
||||
$email->set('from', 'test@test.com');
|
||||
$email->set('status', 'Archived');
|
||||
$entityManager->saveEntity($email);
|
||||
|
||||
$emailService = $this->getApplication()->getContainer()->get('serviceFactory')->create('Email');
|
||||
|
||||
$result = $emailService->findEntities(array(
|
||||
'where' => array(
|
||||
array(
|
||||
'type' => 'equals',
|
||||
'attribute' => 'emailAddress',
|
||||
'value' => 'test@test.com'
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
$this->assertArrayHasKey('collection', $result);
|
||||
$this->assertEquals(1, count($result['collection']));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user