mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 08:17:18 +08:00
4e205ecf45 SuiteCRM 7.13.1 Release c5da36210d Add new modules to unit tests 6321c09b68 Fix #9870 - Fix log level in ImapHandlerFactory 1a0710f537 Add option to enable legacy email compose behaviour eaff3e776e Remove option to allow user to send as themselves from configuration 7f9c743c78 Only display outbound and system accounts on email compose from de1b0f2ad8 Update email compose from dropdown ed30b7575c Allow email compose to send using outbound-only accounts d3a58bd3e0 Fix #9878 - Catch unhandled error in imap2_close cc80eabbfd Fix #9878 - Fix InboundEmail save trim error 9190b40c48 Fix #9878 - Fix InboundEmail save error 0db3827d98 Fix #9878 - Fix relate select popup white screen error e8af74ba7b Fix #9878 - Fix undefined index notice git-subtree-dir: public/legacy git-subtree-split: 4e205ecf45d493ad1351c51f8ce787fa1c6400c9
1052 lines
36 KiB
PHP
1052 lines
36 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* SugarCRM Community Edition is a customer relationship management program developed by
|
|
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
|
|
*
|
|
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
|
|
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation with the addition of the following permission added
|
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
|
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
|
|
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
*
|
|
* This program 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 Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
* this program; if not, see http://www.gnu.org/licenses or write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*
|
|
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
|
|
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
|
|
*
|
|
* 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 Affero General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "Powered by
|
|
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
|
|
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
|
|
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
|
|
*/
|
|
|
|
if (!defined('sugarEntry') || !sugarEntry) {
|
|
die('Not A Valid Entry Point');
|
|
}
|
|
|
|
use SuiteCRM\Utility\SuiteValidator;
|
|
|
|
include_once 'include/Exceptions/SugarControllerException.php';
|
|
|
|
include_once __DIR__ . '/EmailsDataAddressCollector.php';
|
|
include_once __DIR__ . '/EmailsControllerActionGetFromFields.php';
|
|
|
|
class EmailsController extends SugarController
|
|
{
|
|
const ERR_INVALID_INBOUND_EMAIL_TYPE = 100;
|
|
const ERR_STORED_OUTBOUND_EMAIL_NOT_SET = 101;
|
|
const ERR_STORED_OUTBOUND_EMAIL_ID_IS_INVALID = 102;
|
|
const ERR_STORED_OUTBOUND_EMAIL_NOT_FOUND = 103;
|
|
const ERR_REPLY_TO_ADDR_NOT_FOUND = 110;
|
|
const ERR_REPLY_TO_FROMAT_INVALID_SPLITS = 111;
|
|
const ERR_REPLY_TO_FROMAT_INVALID_NO_NAME = 112;
|
|
const ERR_REPLY_TO_FROMAT_INVALID_NO_ADDR = 113;
|
|
const ERR_REPLY_TO_FROMAT_INVALID_AS_FROM = 114;
|
|
|
|
/**
|
|
* @var Email $bean ;
|
|
*/
|
|
public $bean;
|
|
|
|
/**
|
|
* @see EmailsController::composeBean()
|
|
*/
|
|
const COMPOSE_BEAN_MODE_UNDEFINED = 0;
|
|
|
|
/**
|
|
* @see EmailsController::composeBean()
|
|
*/
|
|
const COMPOSE_BEAN_MODE_REPLY_TO = 1;
|
|
|
|
/**
|
|
* @see EmailsController::composeBean()
|
|
*/
|
|
const COMPOSE_BEAN_MODE_REPLY_TO_ALL = 2;
|
|
|
|
/**
|
|
* @see EmailsController::composeBean()
|
|
*/
|
|
const COMPOSE_BEAN_MODE_FORWARD = 3;
|
|
|
|
/**
|
|
* @see EmailsController::composeBean()
|
|
*/
|
|
const COMPOSE_BEAN_WITH_PDF_TEMPLATE = 4;
|
|
|
|
protected static $doNotImportFields = array(
|
|
'action',
|
|
'type',
|
|
'send',
|
|
'record',
|
|
'from_addr_name',
|
|
'reply_to_addr',
|
|
'to_addrs_names',
|
|
'cc_addrs_names',
|
|
'bcc_addrs_names',
|
|
'imap_keywords',
|
|
'raw_source',
|
|
'description',
|
|
'description_html',
|
|
'date_sent_received',
|
|
'message_id',
|
|
'name',
|
|
'status',
|
|
'reply_to_status',
|
|
'mailbox_id',
|
|
'created_by_link',
|
|
'modified_user_link',
|
|
'assigned_user_link',
|
|
'assigned_user_link',
|
|
'uid',
|
|
'msgno',
|
|
'folder',
|
|
'folder_type',
|
|
'inbound_email_record',
|
|
'is_imported',
|
|
'has_attachment',
|
|
'id',
|
|
);
|
|
|
|
/**
|
|
* @see EmailsViewList
|
|
*/
|
|
public function action_index()
|
|
{
|
|
$this->view = 'list';
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewDetaildraft
|
|
*/
|
|
public function action_DetailDraftView()
|
|
{
|
|
$this->view = 'detaildraft';
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewCompose
|
|
*/
|
|
public function action_ComposeView()
|
|
{
|
|
$this->view = 'compose';
|
|
// For viewing the Compose as modal from other modules we need to load the Emails language strings
|
|
if (isset($_REQUEST['in_popup']) && $_REQUEST['in_popup']) {
|
|
if (!is_file('cache/jsLanguage/Emails/' . $GLOBALS['current_language'] . '.js')) {
|
|
require_once('include/language/jsLanguage.php');
|
|
jsLanguage::createModuleStringsCache('Emails', $GLOBALS['current_language']);
|
|
}
|
|
echo '<script src="cache/jsLanguage/Emails/'. $GLOBALS['current_language'] . '.js"></script>';
|
|
}
|
|
if (isset($_REQUEST['ids']) && isset($_REQUEST['targetModule'])) {
|
|
$toAddressIds = explode(',', rtrim($_REQUEST['ids'], ','));
|
|
foreach ($toAddressIds as $id) {
|
|
$destinataryBean = BeanFactory::getBean($_REQUEST['targetModule'], $id);
|
|
if ($destinataryBean) {
|
|
$idLine = '<input type="hidden" class="email-compose-view-to-list" ';
|
|
$idLine .= 'data-record-module="' . $_REQUEST['targetModule'] . '" ';
|
|
$idLine .= 'data-record-id="' . $id . '" ';
|
|
$idLine .= 'data-record-name="' . $destinataryBean->name . '" ';
|
|
$idLine .= 'data-record-email="' . $destinataryBean->email1 . '">';
|
|
echo $idLine;
|
|
}
|
|
}
|
|
}
|
|
if (isset($_REQUEST['relatedModule']) && isset($_REQUEST['relatedId'])) {
|
|
$relateBean = BeanFactory::getBean($_REQUEST['relatedModule'], $_REQUEST['relatedId']);
|
|
$relateLine = '<input type="hidden" class="email-relate-target" ';
|
|
$relateLine .= 'data-relate-module="' . $_REQUEST['relatedModule'] . '" ';
|
|
$relateLine .= 'data-relate-id="' . $_REQUEST['relatedId'] . '" ';
|
|
$relateLine .= 'data-relate-name="' . $relateBean->name . '">';
|
|
echo $relateLine;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a record from the Quick Create Modal
|
|
*/
|
|
public function action_QuickCreate()
|
|
{
|
|
$this->view = 'ajax';
|
|
$originModule = $_REQUEST['module'];
|
|
$targetModule = $_REQUEST['quickCreateModule'];
|
|
|
|
$_REQUEST['module'] = $targetModule;
|
|
|
|
$controller = ControllerFactory::getController($targetModule);
|
|
$controller->loadBean();
|
|
$controller->pre_save();
|
|
$controller->action_save();
|
|
$bean = $controller->bean;
|
|
|
|
$_REQUEST['module'] = $originModule;
|
|
|
|
if (!$bean) {
|
|
$result = ['id' => false];
|
|
echo json_encode($result);
|
|
return;
|
|
}
|
|
|
|
$result = [
|
|
'id' => $bean->id,
|
|
'module' => $bean->module_name,
|
|
];
|
|
echo json_encode($result);
|
|
|
|
if (empty($_REQUEST['parentEmailRecordId'])) {
|
|
return;
|
|
}
|
|
$emailBean = BeanFactory::getBean('Emails', $_REQUEST['parentEmailRecordId']);
|
|
if (!$emailBean) {
|
|
return;
|
|
}
|
|
|
|
$relationship = strtolower($controller->module);
|
|
$emailBean->load_relationship($relationship);
|
|
$emailBean->$relationship->add($bean->id);
|
|
|
|
if (!$bean->load_relationship('emails')) {
|
|
return;
|
|
}
|
|
|
|
$bean->emails->add($emailBean->id);
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewSendemail
|
|
*/
|
|
public function action_send()
|
|
{
|
|
global $current_user;
|
|
global $app_strings;
|
|
|
|
$request = $_REQUEST;
|
|
|
|
$this->bean = $this->bean->populateBeanFromRequest($this->bean, $request);
|
|
$inboundEmailAccount = BeanFactory::newBean('InboundEmail');
|
|
$inboundEmailAccount->retrieve($_REQUEST['inbound_email_id']);
|
|
|
|
if (isset($_REQUEST['from_addr_name']) && !empty($_REQUEST['from_addr_name'])) {
|
|
$this->bean->from_name = $_REQUEST['from_addr_name'];
|
|
$this->bean->from_addr_name = $_REQUEST['from_addr_name'];
|
|
}
|
|
|
|
$outboundEmailAccount = null;
|
|
$useOutbound = false;
|
|
if (!empty($_REQUEST['outbound_email_id'])) {
|
|
/** @var OutboundEmailAccounts $outboundEmailAccount */
|
|
$outboundEmailAccount = BeanFactory::getBean('OutboundEmailAccounts', $_REQUEST['outbound_email_id']);
|
|
|
|
$outboundType = $outboundEmailAccount->type ?? '';
|
|
|
|
if ($outboundType === 'system' || $outboundType === 'system-override') {
|
|
$useOutbound = (new OutboundEmail())->isAllowUserAccessToSystemDefaultOutbound();
|
|
} else {
|
|
$useOutbound = $outboundEmailAccount->ACLAccess('view');
|
|
}
|
|
|
|
$this->bean->from_name = $_REQUEST['from_addr_name'];
|
|
$this->bean->from_addr_name = $_REQUEST['from_addr_name'];
|
|
}
|
|
|
|
if ($useOutbound || $this->userIsAllowedToSendEmail($current_user, $inboundEmailAccount, $this->bean)) {
|
|
$this->bean->save();
|
|
|
|
$this->bean->handleMultipleFileAttachments();
|
|
|
|
// parse and replace bean variables
|
|
$this->bean = $this->replaceEmailVariables($this->bean, $request);
|
|
|
|
if ($useOutbound) {
|
|
$sendResult = $this->bean->sendFromOutbound($outboundEmailAccount);
|
|
} else {
|
|
$sendResult = $this->bean->send();
|
|
}
|
|
|
|
if ($sendResult) {
|
|
$this->bean->status = 'sent';
|
|
$this->bean->save();
|
|
} else {
|
|
// Don't save status if the email is a draft.
|
|
// We need to ensure that drafts will still show
|
|
// in the list view
|
|
if ($this->bean->status !== 'draft') {
|
|
$this->bean->save();
|
|
}
|
|
$this->bean->status = 'send_error';
|
|
}
|
|
|
|
$this->view = 'sendemail';
|
|
} else {
|
|
$GLOBALS['log']->security(
|
|
'User ' . $current_user->name .
|
|
' attempted to send an email using incorrect email account settings in' .
|
|
' which they do not have access to.'
|
|
);
|
|
|
|
$this->view = 'ajax';
|
|
$response['errors'] = [
|
|
'type' => get_class($this->bean),
|
|
'id' => $this->bean->id,
|
|
'title' => $app_strings['LBL_EMAIL_ERROR_SENDING']
|
|
];
|
|
echo json_encode($response);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse and replace bean variables
|
|
* but first validate request,
|
|
* see log to check validation problems
|
|
*
|
|
* return Email bean
|
|
*
|
|
* @param Email $email
|
|
* @param array $request
|
|
* @return Email
|
|
*/
|
|
protected function replaceEmailVariables(Email $email, $request)
|
|
{
|
|
// request validation before replace bean variables
|
|
|
|
if ($this->isValidRequestForReplaceEmailVariables($request)) {
|
|
$macro_nv = array();
|
|
|
|
$focusName = $request['parent_type'];
|
|
$focus = BeanFactory::getBean($focusName, $request['parent_id']);
|
|
if ($email->module_dir == 'Accounts') {
|
|
$focusName = 'Accounts';
|
|
}
|
|
|
|
/**
|
|
* @var EmailTemplate $emailTemplate
|
|
*/
|
|
$emailTemplate = BeanFactory::getBean(
|
|
'EmailTemplates',
|
|
isset($request['emails_email_templates_idb']) ?
|
|
$request['emails_email_templates_idb'] :
|
|
null
|
|
);
|
|
$templateData = $emailTemplate->parse_email_template(
|
|
array(
|
|
'subject' => $email->name,
|
|
'body_html' => $email->description_html,
|
|
'body' => $email->description,
|
|
),
|
|
$focusName,
|
|
$focus,
|
|
$macro_nv
|
|
);
|
|
|
|
$email->name = $templateData['subject'];
|
|
$email->description_html = $templateData['body_html'];
|
|
$email->description = $templateData['body'];
|
|
} else {
|
|
$this->log('Email variables is not replaced because an invalid request.');
|
|
}
|
|
|
|
|
|
return $email;
|
|
}
|
|
|
|
/**
|
|
* Request validation before replace bean variables,
|
|
* see log to check validation problems
|
|
*
|
|
* @param array $request
|
|
* @return bool
|
|
*/
|
|
protected function isValidRequestForReplaceEmailVariables($request)
|
|
{
|
|
$isValidRequestForReplaceEmailVariables = true;
|
|
|
|
if (!is_array($request)) {
|
|
|
|
// request should be an array like standard $_REQUEST
|
|
|
|
$isValidRequestForReplaceEmailVariables = false;
|
|
$this->log('Incorrect request format');
|
|
}
|
|
|
|
|
|
if (!isset($request['parent_type']) || !$request['parent_type']) {
|
|
|
|
// there is no any selected option in 'Related To' field
|
|
// so impossible to replace variables to selected bean data
|
|
|
|
$isValidRequestForReplaceEmailVariables = false;
|
|
$this->log('There isn\'t any selected BEAN-TYPE option in \'Related To\' dropdown');
|
|
}
|
|
|
|
|
|
if (!isset($request['parent_id']) || !$request['parent_id']) {
|
|
|
|
// there is no any selected bean in 'Related To' field
|
|
// so impossible to replace variables to selected bean data
|
|
|
|
$isValidRequestForReplaceEmailVariables = false;
|
|
$this->log('There isn\'t any selected BEAN-ELEMENT in \'Related To\' field');
|
|
}
|
|
|
|
|
|
return $isValidRequestForReplaceEmailVariables;
|
|
}
|
|
|
|
/**
|
|
* Add a message to log
|
|
*
|
|
* @param string $msg
|
|
* @param string $level
|
|
*/
|
|
private function log($msg, $level = 'info')
|
|
{
|
|
$GLOBALS['log']->$level($msg);
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewCompose
|
|
*/
|
|
public function action_SaveDraft()
|
|
{
|
|
$this->bean = $this->bean->populateBeanFromRequest($this->bean, $_REQUEST);
|
|
$this->bean->status = 'draft';
|
|
$this->bean->save();
|
|
$this->bean->handleMultipleFileAttachments();
|
|
$this->view = 'savedraftemail';
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewCompose
|
|
*/
|
|
public function action_DeleteDraft()
|
|
{
|
|
$this->bean->deleted = '1';
|
|
$this->bean->status = 'draft';
|
|
$this->bean->save();
|
|
$this->view = 'deletedraftemail';
|
|
}
|
|
|
|
|
|
/**
|
|
* @see EmailsViewPopup
|
|
*/
|
|
public function action_Popup()
|
|
{
|
|
$this->view = 'popup';
|
|
}
|
|
|
|
/**
|
|
* Gets the values of the "from" field
|
|
* includes the signatures for each account
|
|
*/
|
|
public function action_getFromFields()
|
|
{
|
|
global $current_user;
|
|
global $sugar_config;
|
|
$email = BeanFactory::newBean('Emails');
|
|
$collector = new EmailsDataAddressCollector($current_user, $sugar_config);
|
|
$handler = new EmailsControllerActionGetFromFields($current_user, $collector);
|
|
|
|
$useLegacyEmailConfig = $sugar_config['legacy_email_behaviour'] ?? false;
|
|
if (isTrue($useLegacyEmailConfig)) {
|
|
$ie = BeanFactory::newBean('InboundEmail');
|
|
$results = $handler->handleActionGetFromFields($email, $ie);
|
|
} else {
|
|
$results = $handler->getOutboundFromFields($email);
|
|
}
|
|
|
|
echo $results;
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
/**
|
|
* Returns attachment data to ajax call
|
|
*/
|
|
public function action_GetDraftAttachmentData()
|
|
{
|
|
$data['attachments'] = array();
|
|
|
|
if (!empty($_REQUEST['id'])) {
|
|
$bean = BeanFactory::getBean('Emails', $_REQUEST['id']);
|
|
$data['draft'] = $bean->status == 'draft' ? 1 : 0;
|
|
if (!$attachmentBeans = BeanFactory::getBean('Notes')
|
|
->get_full_list('', "parent_id = '" . $_REQUEST['id'] . "'")) {
|
|
LoggerManager::getLogger()->warn('No attachment Note for selected Email.');
|
|
} else {
|
|
foreach ($attachmentBeans as $attachmentBean) {
|
|
$data['attachments'][] = array(
|
|
'id' => $attachmentBean->id,
|
|
'name' => $attachmentBean->name,
|
|
'file_mime_type' => $attachmentBean->file_mime_type,
|
|
'filename' => $attachmentBean->filename,
|
|
'parent_type' => $attachmentBean->parent_type,
|
|
'parent_id' => $attachmentBean->parent_id,
|
|
'description' => $attachmentBean->description,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$dataEncoded = json_encode(array('data' => $data), JSON_UNESCAPED_UNICODE);
|
|
echo utf8_decode($dataEncoded);
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
public function action_CheckEmail()
|
|
{
|
|
$inboundEmail = BeanFactory::newBean('InboundEmail');
|
|
$inboundEmail->syncEmail();
|
|
|
|
echo json_encode(array('response' => array()));
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
/**
|
|
* Used to list folders in the list view
|
|
*/
|
|
public function action_GetFolders()
|
|
{
|
|
require_once 'include/SugarFolders/SugarFolders.php';
|
|
global $current_user, $mod_strings;
|
|
$email = BeanFactory::newBean('Emails');
|
|
$email->email2init();
|
|
$ie = BeanFactory::newBean('InboundEmail');
|
|
$ie->email = $email;
|
|
$GLOBALS['log']->debug('********** EMAIL 2.0 - Asynchronous - at: refreshSugarFolders');
|
|
$rootNode = new ExtNode('', '');
|
|
$folderOpenState = $current_user->getPreference('folderOpenState', 'Emails');
|
|
$folderOpenState = empty($folderOpenState) ? '' : $folderOpenState;
|
|
|
|
try {
|
|
$ret = $email->et->folder->getUserFolders(
|
|
$rootNode,
|
|
sugar_unserialize($folderOpenState),
|
|
$current_user,
|
|
true
|
|
);
|
|
|
|
$out = json_encode(array('response' => $ret));
|
|
} catch (SugarFolderEmptyException $e) {
|
|
$GLOBALS['log']->warn($e->getMessage());
|
|
$out = json_encode(array('errors' => array($mod_strings['LBL_ERROR_NO_FOLDERS'])));
|
|
}
|
|
|
|
echo $out;
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
|
|
/**
|
|
* @see EmailsViewDetailnonimported
|
|
*/
|
|
public function action_DisplayDetailView()
|
|
{
|
|
$result = null;
|
|
|
|
$db = DBManagerFactory::getInstance();
|
|
$emails = BeanFactory::getBean("Emails");
|
|
|
|
$uid = $_REQUEST['uid'];
|
|
$inboundEmailRecordId = $_REQUEST['inbound_email_record'];
|
|
|
|
$validator = new SuiteValidator();
|
|
|
|
if ($validator->isValidId($uid)) {
|
|
$subQuery = "`mailbox_id` = " . $db->quoted($inboundEmailRecordId) . " AND `uid` = " . $db->quoted($uid);
|
|
$result = $emails->get_full_list('', $subQuery);
|
|
}
|
|
|
|
if (empty($result)) {
|
|
$this->view = 'detailnonimported';
|
|
} else {
|
|
header('location:index.php?module=Emails&action=DetailView&record=' . $result[0]->id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewDetailnonimported
|
|
*/
|
|
public function action_ImportAndShowDetailView()
|
|
{
|
|
$db = DBManagerFactory::getInstance();
|
|
if (isset($_REQUEST['inbound_email_record']) && !empty($_REQUEST['inbound_email_record'])) {
|
|
$inboundEmail = BeanFactory::newBean('InboundEmail');
|
|
$inboundEmail->retrieve($db->quote($_REQUEST['inbound_email_record']), true, true);
|
|
$inboundEmail->connectMailserver();
|
|
$importedEmailId = $inboundEmail->returnImportedEmail($_REQUEST['msgno'], $_REQUEST['uid']);
|
|
|
|
// Set the fields which have been posted in the request
|
|
$this->bean = $this->setAfterImport($importedEmailId, $_REQUEST);
|
|
|
|
if ($importedEmailId !== false) {
|
|
header('location:index.php?module=Emails&action=DetailView&record=' . $importedEmailId);
|
|
}
|
|
} else {
|
|
// When something fail redirect user to index
|
|
header('location:index.php?module=Emails&action=index');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see EmailsViewImport
|
|
*/
|
|
public function action_ImportView()
|
|
{
|
|
$this->view = 'import';
|
|
}
|
|
|
|
public function action_GetCurrentUserID()
|
|
{
|
|
global $current_user;
|
|
echo json_encode(array("response" => $current_user->id));
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
public function action_ImportFromListView()
|
|
{
|
|
$db = DBManagerFactory::getInstance();
|
|
|
|
if (isset($_REQUEST['inbound_email_record']) && !empty($_REQUEST['inbound_email_record'])) {
|
|
$inboundEmail = BeanFactory::getBean('InboundEmail', $db->quote($_REQUEST['inbound_email_record']));
|
|
if (isset($_REQUEST['folder']) && !empty($_REQUEST['folder'])) {
|
|
$inboundEmail->mailbox = $_REQUEST['folder'];
|
|
}
|
|
$inboundEmail->connectMailserver();
|
|
|
|
if (isset($_REQUEST['all']) && $_REQUEST['all'] === 'true') {
|
|
// import all in folder
|
|
$importedEmailsId = $inboundEmail->importAllFromFolder();
|
|
foreach ($importedEmailsId as $importedEmailId) {
|
|
$this->bean = $this->setAfterImport($importedEmailId, $_REQUEST);
|
|
}
|
|
} else {
|
|
foreach ($_REQUEST['uid'] as $uid) {
|
|
$importedEmailId = $inboundEmail->returnImportedEmail($_REQUEST['msgno'], $uid);
|
|
$this->bean = $this->setAfterImport($importedEmailId, $_REQUEST);
|
|
}
|
|
}
|
|
} else {
|
|
$GLOBALS['log']->fatal('EmailsController::action_ImportFromListView() missing inbound_email_record');
|
|
}
|
|
|
|
header('location:index.php?module=Emails&action=index');
|
|
}
|
|
|
|
public function action_ReplyTo()
|
|
{
|
|
$this->composeBean($_REQUEST, self::COMPOSE_BEAN_MODE_REPLY_TO);
|
|
$this->view = 'compose';
|
|
}
|
|
|
|
public function action_ReplyToAll()
|
|
{
|
|
$this->composeBean($_REQUEST, self::COMPOSE_BEAN_MODE_REPLY_TO_ALL);
|
|
$this->view = 'compose';
|
|
}
|
|
|
|
public function action_Forward()
|
|
{
|
|
$this->composeBean($_REQUEST, self::COMPOSE_BEAN_MODE_FORWARD);
|
|
$this->view = 'compose';
|
|
}
|
|
|
|
/**
|
|
* Fills compose view body with the output from PDF Template
|
|
* @see sendEmail::send_email()
|
|
*/
|
|
public function action_ComposeViewWithPdfTemplate()
|
|
{
|
|
$this->composeBean($_REQUEST, self::COMPOSE_BEAN_WITH_PDF_TEMPLATE);
|
|
$this->view = 'compose';
|
|
}
|
|
|
|
public function action_SendDraft()
|
|
{
|
|
$this->view = 'ajax';
|
|
echo json_encode(array());
|
|
}
|
|
|
|
|
|
/**
|
|
* @throws SugarControllerException
|
|
*/
|
|
public function action_MarkEmails()
|
|
{
|
|
$this->markEmails($_REQUEST);
|
|
echo json_encode(array('response' => true));
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
/**
|
|
* @throws SugarControllerException
|
|
*/
|
|
public function action_DeleteFromImap()
|
|
{
|
|
$uid = $_REQUEST['uid'];
|
|
$db = DBManagerFactory::getInstance();
|
|
|
|
if (!empty($_REQUEST['inbound_email_record'])) {
|
|
$emailID = $_REQUEST['inbound_email_record'];
|
|
} elseif (!empty($_REQUEST['record'])) {
|
|
/** @noinspection OneTimeUseVariablesInspection */
|
|
$emailBean = BeanFactory::newBean('Emails');
|
|
$emailID = $emailBean->retrieve($_REQUEST['record']);
|
|
} else {
|
|
throw new SugarControllerException('No Inbound Email record in request');
|
|
}
|
|
|
|
$inboundEmail = BeanFactory::getBean('InboundEmail', $db->quote($emailID));
|
|
|
|
if (is_array($uid)) {
|
|
$uid = implode(',', $uid);
|
|
$this->view = 'ajax';
|
|
}
|
|
|
|
if (isset($uid)) {
|
|
$inboundEmail->deleteMessageOnMailServer($uid);
|
|
} else {
|
|
LoggerManager::getLogger()->fatal('EmailsController::action_DeleteFromImap() missing uid');
|
|
}
|
|
|
|
if ($this->view === 'ajax') {
|
|
echo json_encode(['response' => true]);
|
|
} else {
|
|
header('location:index.php?module=Emails&action=index');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $request
|
|
* @throws SugarControllerException
|
|
*/
|
|
public function markEmails($request)
|
|
{
|
|
// validate the request
|
|
|
|
if (!isset($request['inbound_email_record']) || !$request['inbound_email_record']) {
|
|
throw new SugarControllerException('No Inbound Email record in request');
|
|
}
|
|
|
|
if (!isset($request['folder']) || !$request['folder']) {
|
|
throw new SugarControllerException('No Inbound Email folder in request');
|
|
}
|
|
|
|
// connect to requested inbound email server
|
|
// and select the folder
|
|
|
|
$ie = $this->getInboundEmail($request['inbound_email_record']);
|
|
$ie->mailbox = $request['folder'];
|
|
$ie->connectMailserver();
|
|
|
|
// get requested UIDs and flag type
|
|
|
|
$UIDs = $this->getRequestedUIDs($request);
|
|
$type = $this->getRequestedFlagType($request);
|
|
|
|
// mark emails
|
|
$ie->markEmails($UIDs, $type);
|
|
}
|
|
|
|
/**
|
|
* @param array $request
|
|
* @param int $mode
|
|
* @throws InvalidArgumentException
|
|
* @see EmailsController::COMPOSE_BEAN_MODE_UNDEFINED
|
|
* @see EmailsController::COMPOSE_BEAN_MODE_REPLY_TO
|
|
* @see EmailsController::COMPOSE_BEAN_MODE_REPLY_TO_ALL
|
|
* @see EmailsController::COMPOSE_BEAN_MODE_FORWARD
|
|
*/
|
|
public function composeBean($request, $mode = self::COMPOSE_BEAN_MODE_UNDEFINED)
|
|
{
|
|
if ($mode === self::COMPOSE_BEAN_MODE_UNDEFINED) {
|
|
throw new InvalidArgumentException('EmailController::composeBean $mode argument is COMPOSE_BEAN_MODE_UNDEFINED');
|
|
}
|
|
|
|
$db = DBManagerFactory::getInstance();
|
|
global $mod_strings;
|
|
|
|
|
|
global $current_user;
|
|
$email = BeanFactory::newBean('Emails');
|
|
$email->email2init();
|
|
$ie = BeanFactory::newBean('InboundEmail');
|
|
$ie->email = $email;
|
|
$accounts = $ieAccountsFull = $ie->retrieveAllByGroupIdWithGroupAccounts($current_user->id);
|
|
if (!$accounts) {
|
|
$url = 'index.php?module=Users&action=EditView&record=' . $current_user->id . "&showEmailSettingsPopup=1";
|
|
SugarApplication::appendErrorMessage(
|
|
"You don't have any valid email account settings yet. <a href=\"$url\">Click here to set your email accounts.</a>"
|
|
);
|
|
}
|
|
|
|
|
|
if (isset($request['record']) && !empty($request['record'])) {
|
|
$parent_name = $this->bean->parent_name;
|
|
$this->bean->retrieve($request['record']);
|
|
} else {
|
|
$inboundEmail = BeanFactory::getBean('InboundEmail', $db->quote($request['inbound_email_record']));
|
|
$inboundEmail->connectMailserver();
|
|
$importedEmailId = $inboundEmail->returnImportedEmail($request['msgno'], $request['uid']);
|
|
$this->bean->retrieve($importedEmailId);
|
|
}
|
|
|
|
$_REQUEST['return_module'] = 'Emails';
|
|
$_REQUEST['return_Action'] = 'index';
|
|
|
|
if (isset($parent_name)) {
|
|
$this->bean->parent_name = $parent_name;
|
|
}
|
|
|
|
if ($mode === self::COMPOSE_BEAN_MODE_REPLY_TO || $mode === self::COMPOSE_BEAN_MODE_REPLY_TO_ALL) {
|
|
// Move email addresses from the "from" field to the "to" field
|
|
$this->bean->to_addrs = $this->bean->from_addr;
|
|
isValidEmailAddress($this->bean->to_addrs);
|
|
$this->bean->to_addrs_names = $this->bean->from_addr_name;
|
|
} elseif ($mode === self::COMPOSE_BEAN_MODE_FORWARD) {
|
|
$this->bean->to_addrs = '';
|
|
$this->bean->to_addrs_names = '';
|
|
} elseif ($mode === self::COMPOSE_BEAN_WITH_PDF_TEMPLATE) {
|
|
// Get Related To Field
|
|
// Populate to
|
|
}
|
|
|
|
if ($mode !== self::COMPOSE_BEAN_MODE_REPLY_TO_ALL) {
|
|
$this->bean->cc_addrs_arr = array();
|
|
$this->bean->cc_addrs_names = '';
|
|
$this->bean->cc_addrs = '';
|
|
$this->bean->cc_addrs_ids = '';
|
|
$this->bean->cc_addrs_emails = '';
|
|
}
|
|
|
|
if ($mode === self::COMPOSE_BEAN_MODE_REPLY_TO || $mode === self::COMPOSE_BEAN_MODE_REPLY_TO_ALL) {
|
|
// Add Re to subject
|
|
$this->bean->name = $mod_strings['LBL_RE'] . $this->bean->name;
|
|
} else {
|
|
if ($mode === self::COMPOSE_BEAN_MODE_FORWARD) {
|
|
// Add FW to subject
|
|
$this->bean->name = $mod_strings['LBL_FW'] . $this->bean->name;
|
|
}
|
|
}
|
|
|
|
if (empty($this->bean->name)) {
|
|
$this->bean->name = $mod_strings['LBL_NO_SUBJECT'] . $this->bean->name;
|
|
}
|
|
|
|
// Move body into original message
|
|
if (!empty($this->bean->description_html)) {
|
|
$this->bean->description = '<br>' . $mod_strings['LBL_ORIGINAL_MESSAGE_SEPARATOR'] . '<br>' .
|
|
$this->bean->description_html;
|
|
} else {
|
|
if (!empty($this->bean->description)) {
|
|
$this->bean->description = PHP_EOL . $mod_strings['LBL_ORIGINAL_MESSAGE_SEPARATOR'] . PHP_EOL .
|
|
$this->bean->description;
|
|
}
|
|
}
|
|
|
|
$this->bean->description_html = '';
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $request
|
|
* @return null|string
|
|
*/
|
|
private function getRequestedUIDs($request)
|
|
{
|
|
$ret = $this->getRequestedArgument($request, 'uid');
|
|
if (is_array($ret)) {
|
|
$ret = implode(',', $ret);
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @param array $request
|
|
* @return null|mixed
|
|
*/
|
|
private function getRequestedFlagType($request)
|
|
{
|
|
$ret = $this->getRequestedArgument($request, 'type');
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @param array $request
|
|
* @param string $key
|
|
* @return null|mixed
|
|
*/
|
|
private function getRequestedArgument($request, $key)
|
|
{
|
|
if (!isset($request[$key])) {
|
|
$GLOBALS['log']->error("Requested key is not set: ");
|
|
|
|
return null;
|
|
}
|
|
|
|
return $request[$key];
|
|
}
|
|
|
|
/**
|
|
* return an Inbound Email by requested record
|
|
*
|
|
* @param string $record
|
|
* @return InboundEmail
|
|
* @throws SugarControllerException
|
|
*/
|
|
private function getInboundEmail($record)
|
|
{
|
|
$db = DBManagerFactory::getInstance();
|
|
$ie = BeanFactory::getBean('InboundEmail', $db->quote($record));
|
|
if (!$ie) {
|
|
throw new SugarControllerException("BeanFactory can't resolve an InboundEmail record: $record");
|
|
}
|
|
|
|
return $ie;
|
|
}
|
|
|
|
/**
|
|
* @param array $request
|
|
* @return bool|Email
|
|
* @see Email::id
|
|
* @see EmailsController::action_ImportAndShowDetailView()
|
|
* @see EmailsController::action_ImportView()
|
|
*/
|
|
protected function setAfterImport($importedEmailId, $request)
|
|
{
|
|
$emails = BeanFactory::getBean("Emails", $importedEmailId);
|
|
|
|
foreach ($request as $requestKey => $requestValue) {
|
|
if (strpos($requestKey, 'SET_AFTER_IMPORT_') !== false) {
|
|
$field = str_replace('SET_AFTER_IMPORT_', '', $requestKey);
|
|
if (in_array($field, self::$doNotImportFields)) {
|
|
continue;
|
|
}
|
|
|
|
$emails->{$field} = $requestValue;
|
|
}
|
|
}
|
|
|
|
$emails->save();
|
|
|
|
return $emails;
|
|
}
|
|
|
|
/**
|
|
* @param User $requestedUser
|
|
* @param InboundEmail $requestedInboundEmail
|
|
* @param Email $requestedEmail
|
|
* @return bool false if user doesn't have access
|
|
*/
|
|
protected function userIsAllowedToSendEmail($requestedUser, $requestedInboundEmail, $requestedEmail)
|
|
{
|
|
global $sugar_config;
|
|
|
|
// Check that user is allowed to use inbound email account
|
|
$hasAccessToInboundEmailAccount = false;
|
|
$usersInboundEmailAccounts = $requestedInboundEmail->retrieveAllByGroupIdWithGroupAccounts($requestedUser->id);
|
|
foreach ($usersInboundEmailAccounts as $inboundEmailId => $userInboundEmail) {
|
|
if ($userInboundEmail->id === $requestedInboundEmail->id) {
|
|
$hasAccessToInboundEmailAccount = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$inboundEmailStoredOptions = $requestedInboundEmail->getStoredOptions();
|
|
|
|
// if group email account, check that user is allowed to use group email account
|
|
if ($requestedInboundEmail->isGroupEmailAccount()) {
|
|
if (isTrue($inboundEmailStoredOptions['allow_outbound_group_usage'] ?? false)) {
|
|
$hasAccessToInboundEmailAccount = true;
|
|
} else {
|
|
$hasAccessToInboundEmailAccount = false;
|
|
}
|
|
}
|
|
|
|
// Check that the from address is the same as the inbound email account
|
|
$isFromAddressTheSame = false;
|
|
if ($inboundEmailStoredOptions['from_addr'] === $requestedEmail->from_addr) {
|
|
$isFromAddressTheSame = true;
|
|
}
|
|
|
|
// Check if user is using the system account, as the email address for the system account, will have different
|
|
// settings. If there is not an outbound email id in the stored options then we should try
|
|
// and use the system account, provided that the user is allowed to use to the system account.
|
|
$outboundEmailAccount = new OutboundEmail();
|
|
if (empty($inboundEmailStoredOptions['outbound_email'])) {
|
|
$outboundEmailAccount->getSystemMailerSettings();
|
|
} else {
|
|
$outboundEmailAccount->retrieve($inboundEmailStoredOptions['outbound_email']);
|
|
}
|
|
|
|
$isAllowedToUseOutboundEmail = false;
|
|
if ($outboundEmailAccount->type === 'system') {
|
|
if ($outboundEmailAccount->isAllowUserAccessToSystemDefaultOutbound()) {
|
|
$isAllowedToUseOutboundEmail = true;
|
|
}
|
|
|
|
// When there are not any authentication details for the system account, allow the user to use the system
|
|
// email account.
|
|
if ($outboundEmailAccount->mail_smtpauth_req == 0) {
|
|
$isAllowedToUseOutboundEmail = true;
|
|
}
|
|
|
|
// When the user is allowed to send email as themselves using the system account, allow them to use the system account
|
|
if (isset($sugar_config['email_allow_send_as_user']) && ($sugar_config['email_allow_send_as_user'])) {
|
|
$isAllowedToUseOutboundEmail = true;
|
|
}
|
|
|
|
$admin = BeanFactory::newBean('Administration');
|
|
$admin->retrieveSettings();
|
|
$adminNotifyFromAddress = $admin->settings['notify_fromaddress'];
|
|
if ($adminNotifyFromAddress === $requestedEmail->from_addr) {
|
|
$isFromAddressTheSame = true;
|
|
}
|
|
} else {
|
|
if ($outboundEmailAccount->type === 'user') {
|
|
$isAllowedToUseOutboundEmail = true;
|
|
}
|
|
}
|
|
|
|
// The inbound email account is an empty object, we assume the user has access
|
|
if (empty($requestedInboundEmail->id)) {
|
|
$hasAccessToInboundEmailAccount = true;
|
|
$isFromAddressTheSame = true;
|
|
}
|
|
|
|
$error = false;
|
|
if ($hasAccessToInboundEmailAccount !== true) {
|
|
$error = 'Email Error: Not authorized to use Inbound Account "' . $requestedInboundEmail->name . '"';
|
|
}
|
|
if ($isFromAddressTheSame !== true) {
|
|
$error = 'Email Error: Requested From address mismatch "'
|
|
. $requestedInboundEmail->name . '" / "' . $requestedEmail->from_addr . '"';
|
|
}
|
|
if ($isAllowedToUseOutboundEmail !== true) {
|
|
$error = 'Email Error: Not authorized to use Outbound Account "' . $outboundEmailAccount->name . '"';
|
|
}
|
|
if ($error !== false) {
|
|
$GLOBALS['log']->security($error);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|