SuiteCRM-Core/modules/Emails/EmailUI.php
Clemente Raposo 9d484f189a Squashed 'public/legacy/' changes from 60ccef0448..81ce7933fd
81ce7933fd SuiteCRM 7.13.0 Release
595d2bd92b Fix saveDropDown getting called statically
a70c5d1b99 Fix message deletion after auto import
f57b5ede02 Fix Inbound Email allow_outbound_group_usage filtering
9d38016f59 Remove external_oauth_providers config
d5b4f0025c Remove provider field from ExternalOAuthConnection
d457827c7d Change ExternalOAuthProvider to use open visibility by default for group records
3a51437d3a Add null check
b49d6b79a0 Fix Inbound email tests
277f2c91cb Set default on inbound emails
261dae81c1 Add group to OutboundEmailAccounts menu
3e9dbdd5de Fix ExternalOAuthConnection list view pagination and checkboxes
36cbb6a90c Fix ExternalOAuthProvider list view pagination and checkboxes
fe5f0a10d6 Fix OutboundEmailAccounts list view pagination and checkboxes
b9217a36e2 Fix InboundEmail list view pagination and checkboxes
83bd0024a6 Allow selecting group InboundEmails in folder management
f6a9111b25 Fix inbound email relationship definition
07b3110f8a Filter listview email displayed accounts according to preferences
6c49df527a Remove code that auto-selects a group email none is selected
243a0276af Fix preferences mail folder management dropdown
e7838f0bd4 Fix owner field definition in OutboundEmailAccounts
db9a52a27d Fix bounce mailbox type not being properly set
14d43e1efa Fix compose emails not sending
f9e3677db8 Add optional connection string to InboundEmail module
cef8b2bdb6 Allow setting placeholder for varchar on editview
b2d669a022 Fix group_id not being saved properly on InboundEmail
8d25cc19d8 Fix: From not populating
fae3d7b629 Fix folders not clearing subscription
a8e9eba917 Fix signatures not being editable after being created
1762704a28 Fix menu label in ExternalOAuthConnections module
e97ddb6af0 Fix inbound_emails_external_oauth_connections relationship
2cbebdd5a9 Replace External Oauth Provider sugar_config with module
89f2cee005 Add admin menu entry for ExternalOAuthProviders
dbe91ffe26 Add External providers to ExternalOAuthConnector menu
63b944e033 Add ExternalOAuthProvider module
214f0d9008 Rename ExternalOAuthProvider to ExternalOAuthProviderConnector
972356fa90 Add stringmap field
c701e4c6d5 Add json_decode smarty modifier
49024376be Update Travis build to remove php 7.3 test runs
c23f0e57ac Update Minimum php version to php 7.4
b5914d782e Fix #9670 - Disabling the user profile option about notification of assignments does not work
c2f260e477 Clear caches used by Inline Edition
89de5b620f Fix #99568: Ignore int length when comparing vardefs in newer MySQL versions
fb50e2250e ElasticSearch Indexing batch error handling
32af678e4f Fix #9473 - Missing item "Survey" in campainglog_activity_type_dom
a12f4f37be Fix #9638 - Elasticsearch issue with accented characters
d85d72996d Fix #9499 - Add View Survey Responses Menu item
dcd3ea1091 Fix #9574 - Update method to static for module renaming
c56a5b2188 Fix #9646 - Display TinyMCE in step 4 of the campaigns form wizard
d99439aca1 Fix #9717 - Security Suite Record Group selector doesn't appear when duplicating records
ea0b42469a Add new ACL access logic hooks
be0dec47dc Refactor module access query for extendability
8361cceb60 fix #9802 - Diagnostic checkboxes
44e4f47cca SuiteCRM 7.13.0-beta Release
0d6fef782e Fix #9839 - Make Inbound Email not auditable
c0b93a5d2a Fix #9839 - Align Inbound and Outbound email menu labels
3883c2cb7e Fix #9839 - Inbound email assignment fields save and detail display
0a4fbfa2a9 Fix #9839 - Inbound email filters not displaying
6fefdd79a0 Fix #9839 - OutboundEmailAccounts list filtering
3dcab78053 Fix #9839 - Hide sort fields in search modal if none is sortable
95d099d3b4 Fix #9839 - Always sort emails by date received
aff7965abd Fix #9839 - Add showing sort direction when field is not manually sortable
e3f7d02a65 Fix #9839 - Use previously selected email mailbox when using the navigation links
01c96bf15c Fix #9839 - Add Imap2Handler to use imap2 lib
d8dee51d94 Fix #9839 - Move email list retrieval from InboundEmail to ImapHandler
f798b9af4a Fix #9839 - Update InboundEmail calls to mailparser to send 2nd argument
68a8fa97dd Fix #9839 - Move stream validity check to ImapHandlers
fb4f1c81ec Fix #9839 - Fix InboundEmail pagination totals
dc26951a35 Fix #9839 - Use previously selected email mailbox when using the navigation links
cca80e945b Fix #9839 - Move case macro input to Case module settings admin menu
0fb1075aa2 Fix #9839 - Fix undefined error in listview displayEmptyDataMessages
990e98e4bc Fix #9839 - Add support to test oauth connection in InboundEmail
7259c9ce79 Fix #9839 - Allow refreshing ExternalOAuthConnection tokens
49c5c0ee09 Fix #9839 - Make ExternalOAuthConnection sensitive fields db_encrypted
17bbeb3683 Fix #9839 - Allow admins to list Group and personal ExternalOAuthConnection
0f559a6223 Fix #9839 - Allow encrypting fields on db by setting db_encrypted
471792d75f Fix #9839 - Apply acl and group check filtering to OutboundEmailAccounts
093f08f27b Fix #9839 - Apply acl and group check filtering to InboundEmail
decdd27b23 Fix #9839 - Apply acl and group check filtering to ExternalOAuthConnection
584e40cfb3 Fix #9839 - Add user acl group action check
07c5b9f021 Fix #9839 - Show no-data message on empty OutboundEmailAccounts listview
b857e4feab Fix #9839 - Show no-data message on empty ExternalOAuthConnection listview
9b6a7c4fcf Fix #9839 - Validate required fields on InboundEmail folder selection
0755e4bc7e Fix #9839 - Mark fields required when changing authType in InboundEmail
55123f4d70 Fix #9839 - Show no-data message on empty InboundEmail listview
059d112349 Fix #9839 - Fix InboundEmail save
a656abd069 Fix #9839 - Make getUserRoleNames a static method
ae49f130a9 Fix #9839 - Add is-value-set data to password field
2964fcbebc Fix #9839 - Add option to disable listview empty data messages
a5794343cb Fix #9839 - Add ExternalOAuthConnection link to admin menu
215413931c Fix #9839 - Align mail related menu entries
ee9e98741d Fix #9839 - Always keep write-only fields in OutboundEmailAccounts
4e0551e9f0 Fix #9839 - Disable massupdate and export in OutboundEmailAccounts bulk actions
bbf351ee5e Fix #9839 - Disable massupdate and export in InboundEmail bulkactions
6871295c66 Fix #9839 - Allow selecting auth type in InboundEmail module
9befcd0ae6 Fix #9839 - Add ExternalOAuthConnection entrypoints
2cee6f1d67 Fix #9839 - Add ExternalOAuthConnection module
70025d5b66 Fix #9839 - Add write-only display option to text fields
25e2443850 Fix #9839 - Add write-only display option to varchar fields
6be8a334b1 Fix #9839 - Allow creating personal outbound email accounts
8bff1fb237 Fix #9839 - Align Outbound email accounts views with Inbound
e396b7373c Fix #9839 - Allow admin to create personal inbound accounts for users
284d0eca76 Fix #9839 - Align inbound email and outbound email list views
186d8e41e4 Fix #9839 - Add Security Groups subpanel to Outbound email accounts
c07f43decb Fix #9839 - Add Security Groups subpanel to Inbound email accounts
87c9eee413 Fix #9839 - Add access checks to personal outbound email accounts
2f76e6a5f5 Fix #9839 - Add access checks to personal inbound email accounts
88adc8135b Fix #9839 - Remove email account configuration from User Settings
33feb0b1c2 Fix #9839 - Allow setting personal InboundEmail signatures
4cb0fd99aa Fix #9839 - Allow setting personal InboundEmail as default
b8cc31d8dd Fix #9839 - Add security groups to OutboundEmailAccounts
70a0daba51 Fix #9839 - Tidy OutboundEmailAccounts view definitions
ca171a4460 Fix #9839 - Remove email_provider_chooser from OutboundEmailAccounts
f8a6105618 Fix #9839 - Add menu definition to OutboundEmailAccounts
7d6cfd6c6c Fix #9839 - Load InboundEmail stored_options into fields on load
f18ec84b47 Fix #9839 - Adjust InboundEmail save to use new fields
54bfb4f72c Fix #9839 - Remove InboundEmail legacy custom views
69d008671e Fix #9839 - Add new menu entries to InboundEmail
d01a196f3b Fix #9839 - Add js code for inboundEmail
701498d007 Fix #9839 - Add standard view definitions to InboundEmail
b5870531ba Fix #9839 - Add fields for stored_options in InboundEmail
8c57b07591 Fix #9839 - Add inbound and outbound email menu links to Users module
a8bc647662 Fix #9839 - Add combinescripts smarty plugin
e4db0fbfd4 Fix #9839 - Add writeonly display mode for password fields
1cc85c2dbf Fix #9839 - Add readonly display mode for enum fields

git-subtree-dir: public/legacy
git-subtree-split: 81ce7933fd9cecc378154ed4eac7eaab5ec1f139
2022-12-21 11:44:57 +00:00

3614 lines
132 KiB
PHP
Executable file

<?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".
*/
use SuiteCRM\Utility\SuiteValidator;
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
require_once("include/utils.php");
require_once("include/ytree/Tree.php");
require_once("include/ytree/ExtNode.php");
require_once("include/SugarFolders/SugarFolders.php");
require_once 'include/Exceptions/SuiteException.php';
class EmailUI
{
public $db;
public $folder; // place holder for SugarFolder object
public $folderStates = array(); // array of folderPath names and their states (1/0)
public $smarty;
public $addressSeparators = array(";", ",");
public $rolloverStyle = "<style>div#rollover {position: relative;float: left;margin: none;text-decoration: none;}div#rollover a:hover {padding: 0;}div#rollover a span {display: none;}div#rollover a:hover span {text-decoration: none;display: block;width: 250px;margin-top: 5px;margin-left: 5px;position: absolute;padding: 10px;color: #333; border: 1px solid #ccc; background-color: #fff; font-size: 12px;z-index: 1000;}</style>\n";
public $groupCss = "<span class='groupInbox'>";
public $cacheTimeouts = array(
'messages' => 86400, // 24 hours
'folders' => 300, // 5 mins
'attachments' => 86400, // 24 hours
);
public $userCacheDir = '';
public $coreDynamicFolderQuery = "SELECT emails.id polymorphic_id, 'Emails' polymorphic_module FROM emails
JOIN emails_text on emails.id = emails_text.email_id
WHERE (type = '::TYPE::' OR status = '::STATUS::') AND assigned_user_id = '::USER_ID::' AND emails.deleted = '0'";
/**
* Setting this to false will prevent the email tick to be appended to the compose email link
*
* @var bool
*/
public $appendTick = true;
/**
* Sole constructor
*/
public function __construct()
{
global $sugar_config;
global $current_user;
$folderStateSerial = $current_user->getPreference('folderOpenState', 'Emails');
if (!empty($folderStateSerial)) {
$this->folderStates = sugar_unserialize($folderStateSerial);
}
$this->smarty = new Sugar_Smarty();
$this->folder = new SugarFolder();
$this->userCacheDir = sugar_cached("modules/Emails/{$current_user->id}");
$this->db = DBManagerFactory::getInstance();
}
///////////////////////////////////////////////////////////////////////////
//// CORE
/**
* Renders the frame for emails
* @throws RuntimeException
*/
public function displayEmailFrame($baseTpl = "modules/Emails/templates/_baseEmail.tpl")
{
require_once("include/OutboundEmail/OutboundEmail.php");
global $app_strings, $app_list_strings;
global $mod_strings;
global $sugar_config;
global $current_user;
global $locale;
global $timedate;
global $theme;
global $sugar_version;
global $sugar_flavor;
global $current_language;
global $server_unique_key;
$this->preflightUserCache();
$ie = BeanFactory::newBean('InboundEmail');
// focus listView
$list = array(
'mbox' => 'Home',
'ieId' => '',
'name' => 'Home',
'unreadChecked' => 0,
'out' => array(),
);
$this->_generateComposeConfigData('email_compose');
//Check quick create module access
$QCAvailableModules = $this->_loadQuickCreateModules();
//Get the quickSearch js needed for assigned user id on Search Tab
require_once('include/QuickSearchDefaults.php');
$qsd = QuickSearchDefaults::getQuickSearchDefaults();
$qsd->setFormName('advancedSearchForm');
$quicksearchAssignedUser = "if(typeof sqs_objects == 'undefined'){var sqs_objects = new Array;}";
$quicksearchAssignedUser .= "sqs_objects['advancedSearchForm_assigned_user_name']=" . json_encode($qsd->getQSUser()) . ";";
$qsd->setFormName('Distribute');
$quicksearchAssignedUser .= "sqs_objects['Distribute_assigned_user_name']=" . json_encode($qsd->getQSUser()) . ";";
$this->smarty->assign('quickSearchForAssignedUser', $quicksearchAssignedUser);
///////////////////////////////////////////////////////////////////////
//// BASIC ASSIGNS
$this->smarty->assign("currentUserId", $current_user->id);
$this->smarty->assign("CURRENT_USER_EMAIL", $current_user->email1);
$this->smarty->assign("currentUserName", $current_user->name);
$this->smarty->assign('yuiPath', 'modules/Emails/javascript/yui-ext/');
$this->smarty->assign('app_strings', $app_strings);
$this->smarty->assign('mod_strings', $mod_strings);
$this->smarty->assign('theme', $theme);
$this->smarty->assign('sugar_config', $sugar_config);
$this->smarty->assign('is_admin', $current_user->is_admin);
$this->smarty->assign('sugar_version', $sugar_version);
$this->smarty->assign('sugar_flavor', $sugar_flavor);
$this->smarty->assign('current_language', $current_language);
$this->smarty->assign('server_unique_key', $server_unique_key);
$this->smarty->assign('qcModules', json_encode($QCAvailableModules));
$extAllDebugValue = "ext-all.js";
$this->smarty->assign('extFileName', $extAllDebugValue);
$useRequestedRecord = false;
if (isset($_REQUEST['record']) && $_REQUEST['record'] && $_REQUEST['record'] != $current_user->id) {
$useRequestedRecord = true;
}
$user = $current_user;
if ($useRequestedRecord) {
$user = $current_user->getRequestedUserRecord();
}
// settings: general
$e2UserPreferences = $this->getUserPreferencesJS($useRequestedRecord);
$emailSettings = $e2UserPreferences['emailSettings'];
///////////////////////////////////////////////////////////////////////
//// USER SETTINGS
// settings: accounts
$cuDatePref = $user->getUserDateTimePreferences();
$this->smarty->assign('dateFormat', $cuDatePref['date']);
$this->smarty->assign(
'dateFormatExample',
str_replace(array("Y", "m", "d"), array("yyyy", "mm", "dd"), $cuDatePref['date'])
);
$this->smarty->assign('calFormat', $timedate->get_cal_date_format());
$this->smarty->assign('TIME_FORMAT', $timedate->get_user_time_format());
$ieAccounts = $ie->retrieveByGroupId($user->id);
$ieAccountsOptions = "<option value=''>{$app_strings['LBL_NONE']}</option>\n";
foreach ($ieAccounts as $k => $v) {
$disabled = (!$v->is_personal) ? "DISABLED" : "";
$group = (!$v->is_personal) ? $app_strings['LBL_EMAIL_GROUP'] . "." : "";
$ieAccountsOptions .= "<option value='{$v->id}' $disabled>{$group}{$v->name}</option>\n";
}
$this->smarty->assign('ieAccounts', $ieAccountsOptions);
$this->smarty->assign('rollover', $this->rolloverStyle);
$protocol = filterInboundEmailPopSelection($app_list_strings['dom_email_server_type']);
$this->smarty->assign('PROTOCOL', get_select_options_with_id($protocol, ''));
$this->smarty->assign(
'MAIL_SSL_OPTIONS',
get_select_options_with_id($app_list_strings['email_settings_for_ssl'], '')
);
$this->smarty->assign('ie_mod_strings', return_module_language($current_language, 'InboundEmail'));
$charsetSelectedValue = isset($emailSettings['defaultOutboundCharset']) ? $emailSettings['defaultOutboundCharset'] : false;
if (!$charsetSelectedValue) {
$charsetSelectedValue = $user->getPreference('default_export_charset', 'global');
if (!$charsetSelectedValue) {
$charsetSelectedValue = $locale->getPrecedentPreference('default_email_charset');
}
}
$charset = array(
'options' => $locale->getCharsetSelect(),
'selected' => $charsetSelectedValue,
);
$this->smarty->assign('charset', $charset);
$emailCheckInterval = array(
'options' => $app_strings['LBL_EMAIL_CHECK_INTERVAL_DOM'],
'selected' => $emailSettings['emailCheckInterval']
);
$this->smarty->assign('emailCheckInterval', $emailCheckInterval);
$this->smarty->assign('attachmentsSearchOptions', $app_list_strings['checkbox_dom']);
$this->smarty->assign('sendPlainTextChecked', ($emailSettings['sendPlainText'] == 1) ? 'CHECKED' : '');
$this->smarty->assign(
'showNumInList',
get_select_options_with_id($app_list_strings['email_settings_num_dom'], $emailSettings['showNumInList'])
);
//// END USER SETTINGS
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//// SIGNATURES
$prependSignature = $user->getPreference('signature_prepend') ?
'true' :
'false';
$defaultSignatureId = $user->getPreference('signature_default');
$this->smarty->assign(
'signatures',
$user->getSignatures(false, $defaultSignatureId, false, 'signature_id')
);
$this->smarty->assign(
'signaturesSettings',
$user->getSignatures(false, $defaultSignatureId, false, 'signature_id')
);
$this->smarty->assign(
'signaturesAccountSettings',
$user->getEmailAccountSignatures(false, $defaultSignatureId, false, 'account_signature_id')
);
$signatureButtons = $user->getSignatureButtons(
'SUGAR.email2.settings.createSignature',
!empty($defaultSignatureId)
);
if (!empty($defaultSignatureId)) {
$signatureButtons = $signatureButtons . '<span name="delete_sig" id="delete_sig" style="visibility:inherit;"><input class="button" onclick="SUGAR.email2.settings.deleteSignature();" value="' . $app_strings['LBL_EMAIL_DELETE'] . '" type="button" tabindex="392">&nbsp;
</span>';
} else {
$signatureButtons = $signatureButtons . '<span name="delete_sig" id="delete_sig" style="visibility:hidden;"><input class="button" onclick="SUGAR.email2.settings.deleteSignature();" value="' . $app_strings['LBL_EMAIL_DELETE'] . '" type="button" tabindex="392">&nbsp;
</span>';
}
$this->smarty->assign('signatureButtons', $signatureButtons);
$this->smarty->assign('signaturePrepend', $prependSignature == 'true' ? 'CHECKED' : '');
//// END SIGNATURES
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//// EMAIL TEMPLATES
$email_templates_arr = $this->getEmailTemplatesArray();
natcasesort($email_templates_arr);
$this->smarty->assign('EMAIL_TEMPLATE_OPTIONS', get_select_options_with_id($email_templates_arr, ''));
//// END EMAIL TEMPLATES
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//// FOLDERS & TreeView
$this->smarty->assign(
'groupUserOptions',
$ie->getGroupsWithSelectOptions(array('' => $app_strings['LBL_EMAIL_CREATE_NEW']))
);
$tree = $this->getMailboxNodes();
// preloaded folder
$preloadFolder = 'lazyLoadFolder = ';
$focusFolderSerial = $current_user->getPreference('focusFolder', 'Emails');
if (!empty($focusFolderSerial)) {
$focusFolder = sugar_unserialize($focusFolderSerial);
//$focusFolder['ieId'], $focusFolder['folder']
$preloadFolder .= json_encode($focusFolder) . ";";
} else {
$preloadFolder .= "new Object();";
}
//// END FOLDERS
///////////////////////////////////////////////////////////////////////
$out = "";
$out .= $this->smarty->fetch($baseTpl);
$out .= $tree->generate_header();
$out .= $tree->generateNodesNoInit(true, 'email2treeinit');
$out .= <<<eoq
<script type="text/javascript" language="javascript">
var loader = new YAHOO.util.YUILoader({
require : [
"layout", "element", "tabview", "menu",
"cookie", "sugarwidgets"
],
loadOptional: true,
skin: { base: 'blank', defaultSkin: '' },
onSuccess: email2init,
allowRollup: true,
base: "include/javascript/yui/build/"
});
loader.addModule({
name :"sugarwidgets",
type : "js",
fullpath: "include/javascript/sugarwidgets/SugarYUIWidgets.js",
varName: "YAHOO.SUGAR",
requires: ["datatable", "dragdrop", "treeview", "tabview", "calendar"]
});
loader.insert();
{$preloadFolder};
</script>
eoq;
return $out;
}
/**
* Generate the frame needed for the quick compose email UI. This frame is loaded dynamically
* by an ajax call.
*
* @return JSON An object containing html markup and js script variables.
*/
public function displayQuickComposeEmailFrame()
{
$this->preflightUserCache();
$this->_generateComposeConfigData('email_compose_light');
$javascriptOut = $this->smarty->fetch("modules/Emails/templates/_baseConfigData.tpl");
$divOut = $this->smarty->fetch("modules/Emails/templates/overlay.tpl");
$divOut .= $this->smarty->fetch("modules/Emails/templates/addressSearchContent.tpl");
$outData = array('jsData' => $javascriptOut, 'divData' => $divOut);
$out = json_encode($outData);
return $out;
}
/**
* Load the modules from the metadata file and include in a custom one if it exists
*
* @return array
*/
protected function _loadQuickCreateModules()
{
$QCAvailableModules = array();
$QCModules = array();
include('modules/Emails/metadata/qcmodulesdefs.php');
if (file_exists('custom/modules/Emails/metadata/qcmodulesdefs.php')) {
include('custom/modules/Emails/metadata/qcmodulesdefs.php');
}
foreach ($QCModules as $module) {
$seed = SugarModule::get($module)->loadBean();
if (($seed instanceof SugarBean) && $seed->ACLAccess('edit')) {
$QCAvailableModules[] = $module;
}
}
return $QCAvailableModules;
}
/**
* Given an email link url (eg. index.php?action=Compose&parent_type=Contacts...) break up the
* request components and create a compose package that can be used by the quick compose UI. The
* result is typically passed into the js call SUGAR.quickCompose.init which initalizes the quick compose
* UI.
*
* @param String $emailLinkUrl
* @return JSON Object containing the composePackage and full link url
*/
public function generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, $lazyLoad = false)
{
$composeData = explode("&", $emailLinkUrl);
$a_composeData = array();
foreach ($composeData as $singleRequest) {
$tmp = explode("=", $singleRequest);
$a_composeData[$tmp[0]] = urldecode($tmp[1]);
}
return $this->generateComposePackageForQuickCreate($a_composeData, $emailLinkUrl, $lazyLoad);
}
/**
* @param string $module_name
* @param string $record_id
* @param string $name
* @param string $addr
* @param string $text
* @return string
*/
private function createEmailLink($module_name, $record_id, $name, $addr, $text)
{
global $current_user;
if ($current_user->getEmailClient() == 'sugar') {
$html =<<<HTML
<a class="email-link" href="mailto:{$addr}"
onclick="$(document).openComposeViewModal(this);"
data-module="{$module_name}" data-record-id="{$record_id}"
data-module-name="{$name}" data-email-address="{$addr}"
>{$text}</a>
HTML;
} else {
$html =<<<HTML
<a class="email-link" href="mailto:{$addr}">{$text}</a>
HTML;
}
return $html;
}
/**
*
* @global SugarBean $focus
* @param SugarBean|null $bean
* @param string $emailField
* @param bool $checkAllEmail
* @param string|null $innerText
* @param string|null $composeData
* @return string
* @throws RuntimeException
* @throws InvalidArgumentException
*/
public function populateComposeViewFields(
$bean = null,
$emailField = 'email1',
$checkAllEmail = true,
$innerText = null,
$composeData = null
) {
global $focus;
$myBean = $focus;
$configurator = new Configurator();
$enableConfirmedOptIn = null;
if (isset($configurator->config['email_enable_confirm_opt_in'])) {
$enableConfirmedOptIn = $configurator->config['email_enable_confirm_opt_in'];
} else {
LoggerManager::getLogger()->warn('EmailUI::populateComposeViewFields: $configurator->config[email_enable_confirm_opt_in] is not set');
}
if (!empty($bean)) {
$myBean = $bean;
} else {
$GLOBALS['log']->warn('EmailUI::populateComposeViewFields - $bean is empty');
}
$emailLink = $this->createEmailLink(
$myBean->module_name,
$myBean->id,
$myBean->name,
'',
$innerText
);
// focus is set?
if (!is_object($myBean)) {
$GLOBALS['log']->warn('incorrect bean');
} else {
if (is_array($emailField)) {
$emailFields = $emailField;
} else {
$emailFields = array($emailField);
}
if ($checkAllEmail) {
$i = 1;
$emailField = 'email' . $i;
while (isset($myBean->{$emailField})) {
$emailFields[] = $emailField;
$i++;
$emailField = 'email' . $i;
}
$emailFields = array_unique($emailFields);
}
foreach ($emailFields as $emailField) {
if (!empty($composeData)) {
$emailLink = $this->createEmailLink(
$composeData['parent_type'],
$composeData['parent_id'],
$composeData['parent_name'],
$composeData['to_addrs'],
''
);
} elseif (is_object($myBean) && (property_exists($myBean, $emailField))) {
$email_tick = $this->getEmailAddressConfirmOptInTick($myBean, $emailField);
$optOut = false;
$invalid = false;
if ($enableConfirmedOptIn === SugarEmailAddress::COI_STAT_DISABLED) {
$emailLink = $this->createEmailLink(
$myBean->module_name,
$myBean->id,
$myBean->name,
$myBean->{$emailField},
$myBean->{$emailField}
);
return $emailLink;
}
if (isset($myBean->emailAddress->addresses)) {
if (
isset($myBean->emailAddress)
&& isset($myBean->emailAddress->addresses)
) {
$addresses = $myBean->emailAddress->addresses;
foreach ($addresses as $address) {
if ($address['email_address'] === $myBean->{$emailField}) {
if (!empty($myBean->id)) {
$myBean->retrieve();
}
if ((int)$address['opt_out'] === 1) {
$optOut = true;
}
if ((int)$address['invalid_email'] === 1) {
$invalid = true;
}
if (
$optOut === true
|| $invalid === true
) {
$emailText = '';
if ($this->appendTick) {
$emailText .= $email_tick;
}
$emailText .= '<span class="email-line-through">';
$emailText .= $myBean->{$emailField};
$emailLink .= '</span>';
$emailLink = $this->createEmailLink(
$myBean->module_name,
$myBean->id,
$myBean->name,
$myBean->{$emailField},
$emailText
);
} else {
$emailText = '';
if ($this->appendTick) {
$emailText .= $email_tick;
}
$emailText .= $myBean->{$emailField};
$emailLink = $this->createEmailLink(
$myBean->module_name,
$myBean->id,
$myBean->name,
$myBean->{$emailField},
$emailText
);
}
return $emailLink;
}
}
}
} else {
$GLOBALS['log']->warn(get_class($myBean) . ' does not have email1 field');
}
}
}
return $emailLink;
}
}
/**
* @return string
*/
public function createBulkActionEmailLink()
{
global $app_strings;
$emailLink = '<a class="email-link" href="javascript:void(0);"'
. ' onclick="$(document).openComposeViewModal(this);"'
. ' data-module="" data-record-id="" data-module-name="" data-email-address="">';
$emailLink .= $app_strings['LBL_EMAIL_COMPOSE'];
$emailLink .= '</a>';
return $emailLink;
}
/**
*
* @param Basic|Object $myBean
* @param string $emailField
* @throws RuntimeException
* @throws InvalidArgumentException
* @return string
*/
private function getEmailAddressConfirmOptInTick($myBean, $emailField)
{
$log = LoggerManager::getLogger();
$tick = '';
if ($myBean instanceof Basic) {
$emailAddress = $myBean->getEmailAddressFromEmailField($emailField);
if ($emailAddress instanceof SugarEmailAddress) {
$tick = $emailAddress->getOptInStatusTickHTML();
} else {
$log->warn('Trying to get an email field of non-Basic object');
}
} else {
$log->warn('Trying to get an email field of non-Basic object');
}
return $tick;
}
/**
* Generate the composePackage for the quick compose email UI. The package contains
* key/value pairs generated by the Compose.php file which are then set into the
* quick compose email UI (eg. to addr, parent id, parent type, etc)
*
* @param Array $composeData Associative array read and processed by generateComposeDataPackage.
* @param String $fullLinkUrl A link that contains all pertinant information so the user can be
* directed to the full compose screen if needed
* @param SugarBean $bean Optional - the parent object bean with data
* @return JSON Object containg composePackage and fullLinkUrl
*/
public function generateComposePackageForQuickCreate($composeData, $fullLinkUrl, $lazyLoad = false, $bean = null)
{
$_REQUEST['forQuickCreate'] = true;
if (!$lazyLoad) {
require_once('modules/Emails/Compose.php');
$composePackage = generateComposeDataPackage($composeData, false, $bean);
} else {
$composePackage = $composeData;
}
// JSON object is passed into the function defined within the a href onclick event
// which is delimeted by '. Need to escape all single quotes and &, <, >
// but not double quotes since json would escape them
foreach ($composePackage as $key => $singleCompose) {
if (is_string($singleCompose)) {
$composePackage[$key] = str_replace("&nbsp;", " ", from_html($singleCompose));
}
}
$quickComposeOptions = array('fullComposeUrl' => $fullLinkUrl, 'composePackage' => $composePackage);
$j_quickComposeOptions = JSON::encode($quickComposeOptions, false, true);
return $j_quickComposeOptions;
}
/**
* Generate the config data needed for the Full Compose UI and the Quick Compose UI. The set of config data
* returned is the minimum set needed by the quick compose UI.
*
* @param String $type Drives which tinyMCE options will be included.
* @throws RuntimeException
*/
public function _generateComposeConfigData($type = "email_compose_light")
{
global $app_list_strings, $current_user, $app_strings, $mod_strings, $current_language, $locale;
//Link drop-downs
$parent_types = $app_list_strings['record_type_display'];
$disabled_parent_types = ACLController::disabledModuleList($parent_types, false, 'list');
foreach ($disabled_parent_types as $disabled_parent_type) {
unset($parent_types[$disabled_parent_type]);
}
asort($parent_types);
$linkBeans = json_encode(get_select_options_with_id($parent_types, ''));
//TinyMCE Config
require_once("include/SugarTinyMCE.php");
$tiny = new SugarTinyMCE();
$tinyConf = $tiny->getConfig($type);
//Generate Language Packs
$lang = "var app_strings = new Object();\n";
foreach ($app_strings as $k => $v) {
if (strpos($k, 'LBL_EMAIL_') !== false) {
$vJS = json_encode($v);
$lang .= "app_strings.{$k} = {$vJS};\n";
}
}
//Get the email mod strings but don't use the global variable as this may be overridden by
//other modules when the quick create is rendered.
$email_mod_strings = return_module_language($current_language, 'Emails');
$modStrings = "var mod_strings = new Object();\n";
foreach ($email_mod_strings as $k => $v) {
$v = str_replace("'", "\'",str_replace("\\'", "'", $v));
$modStrings .= "mod_strings.{$k} = '{$v}';\n";
}
$lang .= "\n\n{$modStrings}\n";
//Grab the Inboundemail language pack
$ieModStrings = "var ie_mod_strings = new Object();\n";
$ie_mod_strings = return_module_language($current_language, 'InboundEmail');
foreach ($ie_mod_strings as $k => $v) {
$v = str_replace("'", "\'", $v);
$ieModStrings .= "ie_mod_strings.{$k} = '{$v}';\n";
}
$lang .= "\n\n{$ieModStrings}\n";
$this->smarty->assign('linkBeans', $linkBeans);
$this->smarty->assign('linkBeansOptions', $parent_types);
$this->smarty->assign('tinyMCE', $tinyConf);
$this->smarty->assign('lang', $lang);
$this->smarty->assign('app_strings', $app_strings);
$this->smarty->assign('mod_strings', $email_mod_strings);
$ie1 = BeanFactory::newBean('InboundEmail');
//Signatures
$defsigID = $current_user->getPreference('signature_default');
$defaultSignature = $current_user->getDefaultSignature();
$sigJson = !empty($defaultSignature) ? json_encode(array($defaultSignature['id'] => from_html($defaultSignature['signature_html']))) : "new Object()";
$this->smarty->assign('defaultSignature', $sigJson);
$this->smarty->assign('signatureDefaultId', (isset($defaultSignature['id'])) ? $defaultSignature['id'] : "");
//User Preferences
$this->smarty->assign('userPrefs', json_encode($this->getUserPrefsJS()));
$useRequestedRecord = false;
if (isset($_REQUEST['record']) && $_REQUEST['record'] && $_REQUEST['record'] != $current_user->id) {
$useRequestedRecord = true;
}
$user = $current_user;
if ($useRequestedRecord) {
$user = $current_user->getRequestedUserRecord();
}
$defaultSignature = $user->getDefaultSignature();
$sigJson = !empty($defaultSignature) ?
json_encode(array($defaultSignature['id'] => from_html($defaultSignature['signature_html']))) :
'new Object()';
$this->smarty->assign('defaultSignature', $sigJson);
$this->smarty->assign(
'signatureDefaultId',
isset($defaultSignature['id']) ? $defaultSignature['id'] : ''
);
//User Preferences
$this->smarty->assign(
'userPrefs',
json_encode($this->getUserPreferencesJS($useRequestedRecord))
);
//Get the users default outbound id
$defaultOutID = $ie1->getUsersDefaultOutboundServerId($user);
$this->smarty->assign('defaultOutID', $defaultOutID);
//Character Set
$charsets = json_encode($locale->getCharsetSelect());
$this->smarty->assign('emailCharsets', $charsets);
//Relateable List of People for address book search
//#20776 jchi
$peopleTables = array(
"users",
"contacts",
"leads",
"prospects",
"accounts"
);
$filterPeopleTables = array();
global $app_list_strings, $app_strings;
$filterPeopleTables['LBL_DROPDOWN_LIST_ALL'] = $app_strings['LBL_DROPDOWN_LIST_ALL'];
foreach ($peopleTables as $table) {
$module = ucfirst($table);
$class = substr($module, 0, strlen($module) - 1);
require_once("modules/{$module}/{$class}.php");
$person = new $class();
if (!$person->ACLAccess('list')) {
continue;
}
$filterPeopleTables[$person->table_name] = $app_list_strings['moduleList'][$person->module_dir];
}
$this->smarty->assign('listOfPersons', get_select_options_with_id($filterPeopleTables, ''));
}
//// END CORE
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//// ADDRESS BOOK
/**
* Retrieves all relationship metadata for a user's address book
* @return array
*/
public function getContacts()
{
global $current_user;
$q = "SELECT * FROM address_book WHERE assigned_user_id = '{$current_user->id}' ORDER BY bean DESC";
$r = $this->db->query($q);
$ret = array();
while ($a = $this->db->fetchByAssoc($r)) {
$ret[$a['bean_id']] = array(
'id' => $a['bean_id'],
'module' => $a['bean'],
);
}
return $ret;
}
/**
* Saves changes to a user's address book
* @param array contacts
* @throws SuiteException
*/
public function setContacts($contacts)
{
global $current_user;
$oldContacts = $this->getContacts();
foreach ($contacts as $cid => $contact) {
if (!in_array($contact['id'], $oldContacts, true)) {
$contactId = $contact['id'];
$isValidator = new SuiteValidator();
if (!$isValidator->isValidId($contactId)) {
throw new SuiteException('Invalid contact ID: ' . $contactId);
}
$q = "INSERT INTO address_book (assigned_user_id, bean, bean_id) VALUES ('{$current_user->id}', '{$contact['module']}', '{$contact['id']}')";
$r = $this->db->query($q, true);
}
}
}
/**
* Removes contacts from the user's address book
* @param array ids
* @throws SuiteException
*/
public function removeContacts($ids)
{
global $current_user;
$concat = '';
foreach ($ids as $id) {
if (!empty($concat)) {
$concat .= ', ';
}
$isValidator = new SuiteValidator();
if (!$isValidator->isValidId($id)) {
throw new SuiteException('Invalid contact ID' . $id);
}
$concat .= "'{$id}'";
}
$q = "DELETE FROM address_book WHERE assigned_user_id = '{$current_user->id}' AND bean_id IN ({$concat})";
$r = $this->db->query($q);
}
/**
* saves editted Contact info
* @param string $str JSON serialized object
*/
public function saveContactEdit($str)
{
$json = getJSONobj();
$str = from_html($str);
$obj = $json->decode($str);
$contact = BeanFactory::newBean('Contacts');
$contact->retrieve($obj['contact_id']);
$contact->first_name = $obj['contact_first_name'];
$contact->last_name = $obj['contact_last_name'];
$contact->save();
// handle email address changes
$addresses = array();
foreach ($obj as $k => $req) {
if (strpos($k, 'emailAddress') !== false) {
$addresses[$k] = $req;
}
}
// prefill some REQUEST vars for emailAddress save
$_REQUEST['emailAddressOptOutFlag'] = $obj['optOut'];
$_REQUEST['emailAddressInvalidFlag'] = $obj['invalid'];
$contact->emailAddress->saveEmail($obj['contact_id'], 'Contacts', $addresses, $obj['primary'], '');
}
/**
* Prepares the Edit Contact mini-form via template assignment
* @param string id ID of contact in question
* @param string module Module in focus
* @return array
*/
public function getEditContact($id, $module)
{
global $app_strings;
if (!class_exists("Contact")) {
}
$contact = BeanFactory::newBean('Contacts');
$contact->retrieve($_REQUEST['id']);
$ret = array();
if ($contact->ACLAccess('edit')) {
$contactMeta = array();
$contactMeta['id'] = $contact->id;
$contactMeta['module'] = $contact->module_dir;
$contactMeta['first_name'] = $contact->first_name;
$contactMeta['last_name'] = $contact->last_name;
$this->smarty->assign("app_strings", $app_strings);
$this->smarty->assign(
"contact_strings",
return_module_language(get_current_language(), 'Contacts')
);
$this->smarty->assign("contact", $contactMeta);
$ea = new SugarEmailAddress();
$newEmail = $ea->getEmailAddressWidgetEditView($id, $module, true);
$this->smarty->assign("emailWidget", $newEmail['html']);
$ret['form'] = $this->smarty->fetch("modules/Emails/templates/editContact.tpl");
$ret['prefillData'] = $newEmail['prefillData'];
} else {
$id = "";
$ret['form'] = $app_strings['LBL_EMAIL_ERROR_NO_ACCESS'];
$ret['prefillData'] = '{}';
}
$ret['id'] = $id;
$ret['contactName'] = $contact->full_name;
return $ret;
}
/**
* Retrieves a concatenated list of contacts, those with assigned_user_id = user's id and those in the address_book
* table
* @param array $contacts Array of contact types -> IDs
* @param object $user User in focus
* @return array
*/
public function getUserContacts($contacts, $user = null)
{
global $current_user;
global $locale;
if (empty($user)) {
$user = $current_user;
}
$emailAddress = new SugarEmailAddress();
$ret = array();
$union = '';
$modules = array();
foreach ($contacts as $contact) {
if (!isset($modules[$contact['module']])) {
$modules[$contact['module']] = array();
}
$modules[$contact['module']][] = $contact;
}
foreach ($modules as $module => $contacts) {
if (!empty($union)) {
$union .= " UNION ALL ";
}
$table = strtolower($module);
$idsSerial = '';
foreach ($contacts as $contact) {
if (!empty($idsSerial)) {
$idsSerial .= ",";
}
$idsSerial .= "'{$contact['id']}'";
}
$union .= "(SELECT id, first_name, last_name, title, '{$module}' module FROM {$table} WHERE id IN({$idsSerial}) AND deleted = 0 )";
}
if (!empty($union)) {
$union .= " ORDER BY last_name";
}
$r = $user->db->query($union);
while ($a = $user->db->fetchByAssoc($r)) {
$c = array();
$c['name'] = $locale->getLocaleFormattedName(
$a['first_name'],
"<b>{$a['last_name']}</b>",
'',
$a['title'],
'',
$user
);
$c['id'] = $a['id'];
$c['module'] = $a['module'];
$c['email'] = $emailAddress->getAddressesByGUID($a['id'], $a['module']);
$ret[$a['id']] = $c;
}
return $ret;
}
//// END ADDRESS BOOK
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//// EMAIL 2.0 Preferences
/**
* @param bool $useRequestedRecord
* @return array
* @throws RuntimeException
*/
public function getUserPreferencesJS($useRequestedRecord = false)
{
global $current_user;
$user = $current_user;
if ($useRequestedRecord) {
$user = $current_user->getRequestedUserRecord();
}
// sort order per mailbox view
$sortSerial = $user->getPreference('folderSortOrder', 'Emails');
$sortArray = array();
if (!empty($sortSerial)) {
$sortArray = sugar_unserialize($sortSerial);
}
// treeview collapsed/open states
$folderStateSerial = $user->getPreference('folderOpenState', 'Emails');
$folderStates = array();
if (!empty($folderStateSerial)) {
$folderStates = sugar_unserialize($folderStateSerial);
}
// subscribed accounts
$showFolders = sugar_unserialize(base64_decode($user->getPreference('showFolders', 'Emails')));
// general settings
$emailSettings = $user->getPreference('emailSettings', 'Emails');
if (empty($emailSettings)) {
$emailSettings = array();
$emailSettings['emailCheckInterval'] = -1;
$emailSettings['autoImport'] = '';
$emailSettings['alwaysSaveOutbound'] = '1';
$emailSettings['sendPlainText'] = '';
$emailSettings['defaultOutboundCharset'] = $GLOBALS['sugar_config']['default_email_charset'];
$emailSettings['showNumInList'] = 20;
}
// focus folder
$focusFolder = $user->getPreference('focusFolder', 'Emails');
$focusFolder = !empty($focusFolder) ? sugar_unserialize($focusFolder) : array();
// unread only flag
$showUnreadOnly = $user->getPreference('showUnreadOnly', 'Emails');
$listViewSort = array(
"sortBy" => 'date',
"sortDirection" => 'DESC',
);
// signature prefs
$signaturePrepend = $user->getPreference('signature_prepend') ? 'true' : 'false';
$signatureDefault = $user->getPreference('signature_default');
$signatures = array(
'signature_prepend' => $signaturePrepend,
'signature_default' => $signatureDefault
);
// current_user
$user = array(
'emailAddresses' => $user->emailAddress->getAddressesByGUID($user->id, 'Users'),
'full_name' => from_html($user->full_name),
);
$userPreferences = array();
$userPreferences['sort'] = $sortArray;
$userPreferences['folderStates'] = $folderStates;
$userPreferences['showFolders'] = $showFolders;
$userPreferences['emailSettings'] = $emailSettings;
$userPreferences['focusFolder'] = $focusFolder;
$userPreferences['showUnreadOnly'] = $showUnreadOnly;
$userPreferences['listViewSort'] = $listViewSort;
$userPreferences['signatures'] = $signatures;
$userPreferences['current_user'] = $user;
return $userPreferences;
}
public function getUserPrefsJS($useRequestedRecord = false)
{
return $this->getUserPreferencesJS($useRequestedRecord);
}
///////////////////////////////////////////////////////////////////////////
//// FOLDER FUNCTIONS
/**
* Creates a new Sugar folder
* @param string $nodeLabel New sugar folder name
* @param string $parentLabel Parent folder name
*/
public function saveNewFolder($nodeLabel, $parentId, $isGroup = 0)
{
global $current_user;
$this->folder->name = $nodeLabel;
$this->folder->is_group = $isGroup;
$this->folder->parent_folder = ($parentId == 'Home') ? "" : $parentId;
$this->folder->has_child = 0;
$this->folder->created_by = $current_user->id;
$this->folder->modified_by = $current_user->id;
$this->folder->date_modified = $this->folder->date_created = TimeDate::getInstance()->nowDb();
$this->folder->save();
return array(
'action' => 'newFolderSave',
'id' => $this->folder->id,
'name' => $this->folder->name,
'is_group' => $this->folder->is_group,
'is_dynamic' => $this->folder->is_dynamic
);
}
/**
* Saves user sort prefernces
*/
public function saveListViewSortOrder($ieId, $focusFolder, $sortBy, $sortDir)
{
global $current_user;
$sortArray = array();
$sortSerial = $current_user->getPreference('folderSortOrder', 'Emails');
if (!empty($sortSerial)) {
$sortArray = sugar_unserialize($sortSerial);
}
$sortArray[$ieId][$focusFolder]['current']['sort'] = $sortBy;
$sortArray[$ieId][$focusFolder]['current']['direction'] = $sortDir;
$sortSerial = serialize($sortArray);
$current_user->setPreference('folderSortOrder', $sortSerial, '', 'Emails');
}
/**
* Stickies folder collapse/open state
*/
public function saveFolderOpenState($focusFolder, $focusFolderOpen)
{
global $current_user;
$folderStateSerial = $current_user->getPreference('folderOpenState', 'Emails');
$folderStates = array();
if (!empty($folderStateSerial)) {
$folderStates = sugar_unserialize($folderStateSerial);
}
$folderStates[$focusFolder] = $focusFolderOpen;
$newFolderStateSerial = serialize($folderStates);
$current_user->setPreference('folderOpenState', $newFolderStateSerial, '', 'Emails');
}
/**
* saves a folder's view state
*/
public function saveListView($ieId, $folder)
{
global $current_user;
$saveState = array();
$saveState['ieId'] = $ieId;
$saveState['folder'] = $folder;
$saveStateSerial = serialize($saveState);
$current_user->setPreference('focusFolder', $saveStateSerial, '', 'Emails');
}
/**
* Generates cache folder structure
*/
public function preflightEmailCache($cacheRoot)
{
// base
if (!file_exists($cacheRoot)) {
mkdir_recursive(clean_path($cacheRoot));
}
// folders
if (!file_exists($cacheRoot . "/folders")) {
mkdir_recursive(clean_path("{$cacheRoot}/folders"));
}
// messages
if (!file_exists($cacheRoot . "/messages")) {
mkdir_recursive(clean_path("{$cacheRoot}/messages"));
}
// attachments
if (!file_exists($cacheRoot . "/attachments")) {
mkdir_recursive(clean_path("{$cacheRoot}/attachments"));
}
}
public function deleteEmailCacheForFolders($cacheRoot)
{
$filePath = $cacheRoot . "/folders/folders.php";
if (file_exists($filePath)) {
unlink($filePath);
}
}
///////////////////////////////////////////////////////////////////////////
//// IMAP FUNCTIONS
/**
* Identifies subscribed mailboxes and empties the trash
* @param object $ie InboundEmail
*/
public function emptyTrash(&$ie)
{
global $current_user;
$showFolders = sugar_unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
if (is_array($showFolders)) {
foreach ($showFolders as $ieId) {
if (!empty($ieId)) {
$ie->retrieve($ieId);
$ie->emptyTrash();
}
}
}
}
/**
* returns an array of nodes that correspond to IMAP mailboxes.
* @param bool $forceRefresh
* @param User|null $user User
* @return object TreeView object
*/
public function getMailboxNodes($forceRefresh = false, $user = null)
{
global $sugar_config;
global $current_user;
global $app_strings;
if (!$user) {
$user = $current_user;
}
$tree = new Tree("frameFolders");
$tree->tree_style = 'include/ytree/TreeView/css/check/tree.css';
$nodes = array();
$ie = BeanFactory::newBean('InboundEmail');
$refreshOffset = $this->cacheTimeouts['folders']; // 5 mins. this will be set via user prefs
$rootNode = new ExtNode($app_strings['LBL_EMAIL_HOME_FOLDER'], $app_strings['LBL_EMAIL_HOME_FOLDER']);
$rootNode->dynamicloadfunction = '';
$rootNode->expanded = true;
$rootNode->dynamic_load = true;
$showFolders = sugar_unserialize(base64_decode($user->getPreference('showFolders', 'Emails')));
if (empty($showFolders)) {
$showFolders = array();
}
// INBOX NODES
if ($user->hasPersonalEmail()) {
$personals = $ie->retrieveByGroupId($user->id);
foreach ($personals as $k => $personalAccount) {
if (in_array($personalAccount->id, $showFolders)) {
// check for cache value
$cacheRoot = sugar_cached("modules/Emails/{$personalAccount->id}");
$this->preflightEmailCache($cacheRoot);
if ($this->validCacheFileExists($personalAccount->id, 'folders', "folders.php")) {
$mailboxes = $this->getMailBoxesFromCacheValue($personalAccount);
} else {
$mailboxes = $personalAccount->getMailboxes();
}
$acctNode = new ExtNode('Home::' . $personalAccount->name, $personalAccount->name);
$acctNode->dynamicloadfunction = '';
$acctNode->expanded = false;
$acctNode->set_property('cls', 'ieFolder');
$acctNode->set_property('ieId', $personalAccount->id);
$acctNode->set_property('protocol', $personalAccount->protocol);
if (array_key_exists('Home::' . $personalAccount->name, $this->folderStates)) {
if ($this->folderStates['Home::' . $personalAccount->name] == 'open') {
$acctNode->expanded = true;
}
}
$acctNode->dynamic_load = true;
$nodePath = $acctNode->_properties['id'];
foreach ($mailboxes as $k => $mbox) {
$acctNode->add_node($this->buildTreeNode(
$k,
$k,
$mbox,
$personalAccount->id,
$nodePath,
false,
$personalAccount
));
}
$rootNode->add_node($acctNode);
}
}
}
// GROUP INBOX NODES
$beans = $ie->retrieveAllByGroupId($user->id, false);
foreach ($beans as $k => $groupAccount) {
if (in_array($groupAccount->id, $showFolders)) {
// check for cache value
$cacheRoot = sugar_cached("modules/Emails/{$groupAccount->id}");
$this->preflightEmailCache($cacheRoot);
//$groupAccount->connectMailserver();
if ($this->validCacheFileExists($groupAccount->id, 'folders', "folders.php")) {
$mailboxes = $this->getMailBoxesFromCacheValue($groupAccount);
} else {
$mailboxes = $groupAccount->getMailBoxesForGroupAccount();
}
$acctNode = new ExtNode($groupAccount->name, "group.{$groupAccount->name}");
$acctNode->dynamicloadfunction = '';
$acctNode->expanded = false;
$acctNode->set_property('isGroup', 'true');
$acctNode->set_property('ieId', $groupAccount->id);
$acctNode->set_property('protocol', $groupAccount->protocol);
if (array_key_exists('Home::' . $groupAccount->name, $this->folderStates)) {
if ($this->folderStates['Home::' . $groupAccount->name] == 'open') {
$acctNode->expanded = true;
}
}
$acctNode->dynamic_load = true;
$nodePath = $rootNode->_properties['id'] . "::" . $acctNode->_properties['id'];
foreach ($mailboxes as $k => $mbox) {
$acctNode->add_node($this->buildTreeNode(
$k,
$k,
$mbox,
$groupAccount->id,
$nodePath,
true,
$groupAccount
));
}
$rootNode->add_node($acctNode);
}
}
// SugarFolder nodes
/* SugarFolders are built at onload when the UI renders */
$tree->add_node($rootNode);
return $tree;
}
public function getMailBoxesFromCacheValue($mailAccount)
{
$foldersCache = $this->getCacheValue($mailAccount->id, 'folders', "folders.php", 'foldersCache');
$mailboxes = $foldersCache['mailboxes'];
$mailboxesArray = $mailAccount->generateFlatArrayFromMultiDimArray(
$mailboxes,
$mailAccount->retrieveDelimiter()
);
$mailAccount->saveMailBoxFolders($mailboxesArray);
$this->deleteEmailCacheForFolders($cacheRoot);
return $mailboxes;
} // fn
/**
* Builds up a TreeView Node object
* @param mixed
* @param mixed
* @param string
* @param string ID of InboundEmail instance
* @param string nodePath Serialized path from root node to current node
* @param bool isGroup
* @param bool forceRefresh
* @return mixed
*/
public function buildTreeNode($key, $label, $mbox, $ieId, $nodePath, $isGroup, $ie)
{
global $sugar_config;
// get unread counts
$exMbox = explode("::", $nodePath);
$unseen = 0;
$GLOBALS['log']->debug("$key --- $nodePath::$label");
if (count($exMbox) >= 2) {
$mailbox = "";
for ($i = 2; $i < count($exMbox); $i++) {
if ($mailbox != "") {
$mailbox .= ".";
}
$mailbox .= (string)($exMbox[$i]);
}
$mailbox = substr($key, strpos($key, '.'));
$unseen = $this->getUnreadCount($ie, $mailbox);
if ($unseen > 0) {
//$label = " <span id='span{$ie->id}{$ie->mailbox}' style='font-weight:bold'>{$label} (<span id='span{$ie->id}{$ie->mailbox}nums'>{$unseen}</span>)</span>";
}
}
$nodePath = $nodePath . "::" . $label;
$node = new ExtNode($nodePath, $label);
$node->dynamicloadfunction = '';
$node->expanded = false;
$node->set_property('labelStyle', "remoteFolder");
if (array_key_exists($nodePath, $this->folderStates)) {
if ($this->folderStates[$nodePath] == 'open') {
$node->expanded = true;
}
}
$group = ($isGroup) ? 'true' : 'false';
$node->dynamic_load = true;
//$node->set_property('href', " SUGAR.email2.listView.populateListFrame(YAHOO.namespace('frameFolders').selectednode, '{$ieId}', 'false');");
$node->set_property('isGroup', $group);
$node->set_property('isDynamic', 'false');
$node->set_property('ieId', $ieId);
$node->set_property('mbox', $key);
$node->set_property('unseen', $unseen);
$node->set_property('cls', 'ieFolder');
if (is_array($mbox)) {
foreach ($mbox as $k => $v) {
$node->add_node($this->buildTreeNode("$key.$k", $k, $v, $ieId, $nodePath, $isGroup, $ie));
}
}
return $node;
}
/**
* Totals the unread emails
*/
public function getUnreadCount(&$ie, $mailbox)
{
global $sugar_config;
$unseen = 0;
// use cache
return $ie->getCacheUnreadCount($mailbox);
}
///////////////////////////////////////////////////////////////////////////
//// DISPLAY CODE
/**
* Used exclusively by draft code. Returns Notes and Documents as attachments.
* @param array $ret
* @return array
*/
public function getDraftAttachments($ret)
{
$db = DBManagerFactory::getInstance();
// $ret['uid'] is the draft Email object's GUID
$ret['attachments'] = array();
$q = "SELECT id, filename FROM notes WHERE parent_id = '{$ret['uid']}' AND deleted = 0";
$r = $db->query($q);
while ($a = $db->fetchByAssoc($r)) {
$ret['attachments'][$a['id']] = array(
'id' => $a['id'],
'filename' => $a['filename'],
);
}
return $ret;
}
public function createCopyOfInboundAttachment($ie, $ret, $uid)
{
global $sugar_config;
if ($ie->isPop3Protocol()) {
// get the UIDL from database;
$cachedUIDL = md5($uid);
$cache = sugar_cached("modules/Emails/{$ie->id}/messages/{$ie->mailbox}{$cachedUIDL}.php");
} else {
$cache = sugar_cached("modules/Emails/{$ie->id}/messages/{$ie->mailbox}{$uid}.php");
}
if (file_exists($cache)) {
include($cache); // profides $cacheFile
$metaOut = unserialize($cacheFile['out']);
$meta = $metaOut['meta']['email'];
if (isset($meta['attachments'])) {
$attachmentHtmlData = $meta['attachments'];
$actualAttachmentInfo = array();
$this->parseAttachmentInfo($actualAttachmentInfo, $attachmentHtmlData);
if (count($actualAttachmentInfo) > 0) {
foreach ($actualAttachmentInfo as $key => $value) {
$info_vars = array();
parse_str($value, $info_vars);
$fileName = $info_vars['tempName'];
$attachmentid = $info_vars['id'];
$guid = create_guid();
$destination = clean_path("{$this->userCacheDir}/{$guid}");
$attachmentFilePath = sugar_cached("modules/Emails/{$ie->id}/attachments/{$attachmentid}");
copy($attachmentFilePath, $destination);
$ret['attachments'][$guid] = array();
$ret['attachments'][$guid]['id'] = $guid . $fileName;
$ret['attachments'][$guid]['filename'] = $fileName;
} // for
} // if
} // if
} // if
return $ret;
} // fn
public function parseAttachmentInfo(&$actualAttachmentInfo, $attachmentHtmlData)
{
$downLoadPHP = strpos($attachmentHtmlData, "index.php?entryPoint=download&");
while ($downLoadPHP) {
$attachmentHtmlData = substr($attachmentHtmlData, $downLoadPHP + 30);
$final = strpos($attachmentHtmlData, "\">");
$actualAttachmentInfo[] = substr($attachmentHtmlData, 0, $final);
$attachmentHtmlData = substr($attachmentHtmlData, $final);
$downLoadPHP = strpos($attachmentHtmlData, "index.php?entryPoint=download&");
} // while
}
/**
* Renders the QuickCreate form from Smarty and returns HTML
* @param array $vars request variable global
* @param object $email Fetched email object
* @param bool $addToAddressBook
* @return array
*/
public function getQuickCreateForm($vars, $email, $addToAddressBookButton = false)
{
require_once("include/EditView/EditView2.php");
global $app_strings;
global $mod_strings;
global $current_user;
global $beanList;
global $beanFiles;
global $current_language;
//Setup the current module languge
$mod_strings = return_module_language($current_language, $_REQUEST['qc_module']);
$bean = $beanList[$_REQUEST['qc_module']];
$class = $beanFiles[$bean];
require_once($class);
$focus = new $bean();
$people = array(
'Contact'
,
'Lead'
);
$emailAddress = array();
// people
if (in_array($bean, $people)) {
// lead specific
$focus->lead_source = 'Email';
$focus->lead_source_description = trim($email->name);
$from = (isset($email->from_name) && !empty($email->from_name)) ? $email->from_name : $email->from_addr;
if (isset($_REQUEST['sugarEmail']) && !empty($_REQUEST['sugarEmail'])) {
if ($email->status == "sent") {
$from = (isset($email->to_addrs_names) && !empty($email->to_addrs_names)) ? $email->to_addrs_names : $email->to_addrs;
} else {
$from = (isset($email->from_name) && !empty($email->from_name)) ? $email->from_name : $email->from_addr_name;
}
}
$name = explode(" ", trim($from));
$address = trim(array_pop($name));
$address = str_replace(array("<", ">", "&lt;", "&gt;"), "", $address);
isValidEmailAddress($address);
$emailAddress[] = array(
'email_address' => $address,
'primary_address' => 1,
'invalid_email' => 0,
'opt_out' => 0,
'reply_to_address' => 1
);
$focus->email1 = $address;
if (!empty($name)) {
$focus->last_name = trim(array_pop($name));
foreach ($name as $first) {
if (!empty($focus->first_name)) {
$focus->first_name .= " ";
}
$focus->first_name .= trim($first);
}
}
} else {
// bugs, cases, tasks
$focus->name = trim($email->name);
}
$focus->description = trim(strip_tags($email->description));
$focus->assigned_user_id = $current_user->id;
$EditView = new EditView();
$EditView->ss = new Sugar_Smarty();
//MFH BUG#20283 - checks for custom quickcreate fields
$EditView->setup(
$_REQUEST['qc_module'],
$focus,
'custom/modules/' . $focus->module_dir . '/metadata/editviewdefs.php',
'include/EditView/EditView.tpl'
);
$EditView->process();
$EditView->render();
$EditView->defs['templateMeta']['form']['buttons'] = array(
'email2save' => array(
'id' => 'e2AjaxSave',
'customCode' => '<input type="button" class="button" value=" ' . $app_strings['LBL_SAVE_BUTTON_LABEL']
. ' " onclick="SUGAR.email2.detailView.saveQuickCreate(false);" />'
),
'email2saveandreply' => array(
'id' => 'e2SaveAndReply',
'customCode' => '<input type="button" class="button" value=" ' . $app_strings['LBL_EMAIL_SAVE_AND_REPLY']
. ' " onclick="SUGAR.email2.detailView.saveQuickCreate(\'reply\');" />'
),
'email2cancel' => array(
'id' => 'e2cancel',
'customCode' => '<input type="button" class="button" value=" ' . $app_strings['LBL_EMAIL_CANCEL']
. ' " onclick="SUGAR.email2.detailView.quickCreateDialog.hide();" />'
)
);
if ($addToAddressBookButton) {
$EditView->defs['templateMeta']['form']['buttons']['email2saveAddToAddressBook'] = array(
'id' => 'e2addToAddressBook',
'customCode' => '<input type="button" class="button" value=" ' . $app_strings['LBL_EMAIL_ADDRESS_BOOK_SAVE_AND_ADD']
. ' " onclick="SUGAR.email2.detailView.saveQuickCreate(true);" />'
);
}
//Get the module language for javascript
if (!is_file(sugar_cached('jsLanguage/') . $_REQUEST['qc_module'] . '/' . $GLOBALS['current_language'] . '.js')) {
require_once('include/language/jsLanguage.php');
jsLanguage::createModuleStringsCache($_REQUEST['qc_module'], $GLOBALS['current_language']);
}
$jsLanguage = getVersionedScript(
"cache/jsLanguage/{$_REQUEST['qc_module']}/{$GLOBALS['current_language']}.js",
$GLOBALS['sugar_config']['js_lang_version']
);
$EditView->view = 'EmailQCView';
$EditView->defs['templateMeta']['form']['headerTpl'] = 'include/EditView/header.tpl';
$EditView->defs['templateMeta']['form']['footerTpl'] = 'include/EditView/footer.tpl';
$meta = array();
$meta['html'] = $jsLanguage . $EditView->display(false, true);
$meta['html'] = str_replace(
"src='" . getVersionedPath('include/SugarEmailAddress/SugarEmailAddress.js') . "'",
'',
$meta['html']
);
$meta['emailAddress'] = $emailAddress;
$mod_strings = return_module_language($current_language, 'Emails');
return $meta;
}
/**
* Renders the Import form from Smarty and returns HTML
* @param array $vars request variable global
* @param object $email Fetched email object
* @param bool $addToAddressBook
* @return array
*/
public function getImportForm($vars, $email, $formName = 'ImportEditView')
{
require_once("include/EditView/EditView2.php");
require_once("include/TemplateHandler/TemplateHandler.php");
require_once('include/QuickSearchDefaults.php');
$qsd = QuickSearchDefaults::getQuickSearchDefaults();
$qsd->setFormName($formName);
global $app_strings;
global $current_user;
global $app_list_strings;
$sqs_objects = array(
"{$formName}_parent_name" => $qsd->getQSParent(),
);
$smarty = new Sugar_Smarty();
$smarty->assign("APP", $app_strings);
$smarty->assign('formName', $formName);
$showAssignTo = false;
if (!isset($vars['showAssignTo']) || $vars['showAssignTo'] == true) {
$showAssignTo = true;
} // if
if ($showAssignTo) {
if (empty($email->assigned_user_id) && empty($email->id)) {
$email->assigned_user_id = $current_user->id;
}
if (empty($email->assigned_name) && empty($email->id)) {
$email->assigned_user_name = $current_user->user_name;
}
$sqs_objects["{$formName}_assigned_user_name"] = $qsd->getQSUser();
}
$smarty->assign("showAssignedTo", $showAssignTo);
$showDelete = false;
if (!isset($vars['showDelete']) || $vars['showDelete'] == true) {
$showDelete = true;
}
$smarty->assign("showDelete", $showDelete);
$smarty->assign("userId", $email->assigned_user_id);
$smarty->assign("userName", $email->assigned_user_name);
$parent_types = $app_list_strings['record_type_display'];
$smarty->assign('parentOptions', get_select_options_with_id($parent_types, $email->parent_type));
$quicksearch_js = '<script type="text/javascript" language="javascript">sqs_objects = ' . json_encode($sqs_objects) . '</script>';
$smarty->assign('SQS', $quicksearch_js);
$meta = array();
$meta['html'] = $smarty->fetch("modules/Emails/templates/importRelate.tpl");
return $meta;
}
/**
* This function returns the detail view for email in new 2.0 interface
*
*/
public function getDetailViewForEmail2($emailId)
{
require_once('include/DetailView/DetailView.php');
global $app_strings, $app_list_strings;
global $mod_strings;
$smarty = new Sugar_Smarty();
// SETTING DEFAULTS
$focus = BeanFactory::newBean('Emails');
$focus->retrieve($emailId);
$detailView->ss = new Sugar_Smarty();
$detailView = new DetailView();
$title = "";
$offset = 0;
if ($focus->type == 'out') {
$title = getClassicModuleTitle('Emails', array($mod_strings['LBL_SENT_MODULE_NAME'], $focus->name), true);
} elseif ($focus->type == 'draft') {
$title = getClassicModuleTitle(
'Emails',
array($mod_strings['LBL_LIST_FORM_DRAFTS_TITLE'], $focus->name),
true
);
} elseif ($focus->type == 'inbound') {
$title = getClassicModuleTitle('Emails', array($mod_strings['LBL_INBOUND_TITLE'], $focus->name), true);
}
$smarty->assign("emailTitle", $title);
// DEFAULT TO TEXT IF NO HTML CONTENT:
$html = trim(from_html($focus->description_html));
if (empty($html)) {
$smarty->assign('SHOW_PLAINTEXT', 'true');
} else {
$smarty->assign('SHOW_PLAINTEXT', 'false');
}
//if not empty or set to test (from test campaigns)
if (!empty($focus->parent_type) && $focus->parent_type != 'test') {
$smarty->assign('PARENT_MODULE', $focus->parent_type);
$smarty->assign('PARENT_TYPE', $app_list_strings['record_type_display'][$focus->parent_type] . ":");
}
global $gridline;
$smarty->assign('MOD', $mod_strings);
$smarty->assign('APP', $app_strings);
$smarty->assign('GRIDLINE', $gridline);
$smarty->assign('PRINT_URL', 'index.php?' . $GLOBALS['request_string']);
$smarty->assign('ID', $focus->id);
$smarty->assign('TYPE', $focus->type);
$smarty->assign('PARENT_NAME', $focus->parent_name);
$smarty->assign('PARENT_ID', $focus->parent_id);
$smarty->assign('NAME', $focus->name);
$smarty->assign('ASSIGNED_TO', $focus->assigned_user_name);
$smarty->assign('DATE_MODIFIED', $focus->date_modified);
$smarty->assign('DATE_ENTERED', $focus->date_entered);
$smarty->assign('DATE_START', $focus->date_start);
$smarty->assign('TIME_START', $focus->time_start);
$smarty->assign('FROM', $focus->from_addr);
isValidEmailAddress($focus->from_addr);
$smarty->assign('TO', nl2br($focus->to_addrs));
$smarty->assign('CC', nl2br($focus->cc_addrs));
$smarty->assign('BCC', nl2br($focus->bcc_addrs));
$smarty->assign('CREATED_BY', $focus->created_by_name);
$smarty->assign('MODIFIED_BY', $focus->modified_by_name);
$smarty->assign('DATE_SENT_RECEIVED', $focus->date_sent_received);
$smarty->assign('EMAIL_NAME', 'RE: ' . $focus->name);
$smarty->assign("TAG", $focus->listviewACLHelper());
$smarty->assign("SUGAR_VERSION", $GLOBALS['sugar_version']);
$smarty->assign("JS_CUSTOM_VERSION", $GLOBALS['sugar_config']['js_custom_version']);
if (!empty($focus->reply_to_email)) {
$replyTo = "
<tr>
<td class=\"tabDetailViewDL\"><slot>" . $mod_strings['LBL_REPLY_TO_NAME'] . "</slot></td>
<td colspan=3 class=\"tabDetailViewDF\"><slot>" . $focus->reply_to_addr . "</slot></td>
</tr>";
$smarty->assign("REPLY_TO", $replyTo);
}
///////////////////////////////////////////////////////////////////////////////
//// JAVASCRIPT VARS
$jsVars = '';
$jsVars .= "var showRaw = '{$mod_strings['LBL_BUTTON_RAW_LABEL']}';";
$jsVars .= "var hideRaw = '{$mod_strings['LBL_BUTTON_RAW_LABEL_HIDE']}';";
$smarty->assign("JS_VARS", $jsVars);
///////////////////////////////////////////////////////////////////////////////
//// NOTES (attachements, etc.)
///////////////////////////////////////////////////////////////////////////////
$note = BeanFactory::newBean('Notes');
$where = "notes.parent_id='{$focus->id}'";
//take in account if this is from campaign and the template id is stored in the macros.
if (isset($macro_values) && isset($macro_values['email_template_id'])) {
$where = "notes.parent_id='{$macro_values['email_template_id']}'";
}
$notes_list = $note->get_full_list("notes.name", $where, true);
if (!isset($notes_list)) {
$notes_list = array();
}
$attachments = '';
for ($i = 0; $i < count($notes_list); $i++) {
$the_note = $notes_list[$i];
$attachments .= "<a href=\"index.php?entryPoint=download&id={$the_note->id}&type=Notes\">" . $the_note->name . "</a><br />";
$focus->cid2Link($the_note->id, $the_note->file_mime_type);
}
$smarty->assign('DESCRIPTION', nl2br($focus->description));
$smarty->assign('DESCRIPTION_HTML', from_html($focus->description_html));
$smarty->assign("ATTACHMENTS", $attachments);
///////////////////////////////////////////////////////////////////////////////
//// SUBPANELS
///////////////////////////////////////////////////////////////////////////////
$show_subpanels = true;
if ($show_subpanels) {
require_once('include/SubPanel/SubPanelTiles.php');
$subpanel = new SubPanelTiles($focus, 'Emails');
$smarty->assign("SUBPANEL", $subpanel->display());
}
$meta['html'] = $smarty->fetch("modules/Emails/templates/emailDetailView.tpl");
return $meta;
} // fn
/**
* Sets the "read" flag in the overview cache
*/
public function setReadFlag($ieId, $mbox, $uid)
{
$this->markEmails('read', $ieId, $mbox, $uid);
}
/**
* Marks emails with the passed flag type. This will be applied to local
* cache files as well as remote emails.
* @param string $type Flag type
* @param string $ieId
* @param string $folder IMAP folder structure or SugarFolder GUID
* @param string $uids Comma sep list of UIDs or GUIDs
*/
public function markEmails($type, $ieId, $folder, $uids)
{
global $app_strings;
$uids = $this->_cleanUIDList($uids);
$exUids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
if (strpos($folder, 'sugar::') !== false) {
// dealing with a sugar email object, uids are GUIDs
foreach ($exUids as $id) {
$email = BeanFactory::newBean('Emails');
$email->retrieve($id);
// BUG FIX BEGIN
// Bug 50973 - marking unread in group inbox removes message
if (empty($email->assigned_user_id)) {
$email->setFieldNullable('assigned_user_id');
}
// BUG FIX END
switch ($type) {
case "unread":
$email->status = 'unread';
$email->save();
break;
case "read":
$email->status = 'read';
$email->save();
break;
case "deleted":
$email->delete();
break;
case "flagged":
$email->flagged = 1;
$email->save();
break;
case "unflagged":
$email->flagged = 0;
$email->save();
break;
}
// BUG FIX BEGIN
// Bug 50973 - reset assigned_user_id field defs
if (empty($email->assigned_user_id)) {
$email->revertFieldNullable('assigned_user_id');
}
// BUG FIX END
}
} else {
/* dealing with IMAP email, uids are IMAP uids */
global $ie; // provided by EmailUIAjax.php
if (empty($ie)) {
$ie = BeanFactory::newBean('InboundEmail');
}
$ie->retrieve($ieId);
$ie->mailbox = $folder;
$ie->connectMailserver();
// mark cache files
if ($type == 'deleted') {
$ie->deleteMessageOnMailServer($uids);
$ie->deleteMessageFromCache($uids);
} else {
$overviews = $ie->getCacheValueForUIDs($ie->mailbox, $exUids);
$manipulated = array();
foreach ($overviews['retArr'] as $k => $overview) {
if (in_array($overview->uid, $exUids)) {
switch ($type) {
case "unread":
$overview->seen = 0;
break;
case "read":
$overview->seen = 1;
break;
case "flagged":
$overview->flagged = 1;
break;
case "unflagged":
$overview->flagged = 0;
break;
}
$manipulated[] = $overview;
}
}
if (!empty($manipulated)) {
$ie->setCacheValue($ie->mailbox, array(), $manipulated);
/* now mark emails on email server */
$ie->markEmails(implode(",", explode($app_strings['LBL_EMAIL_DELIMITER'], $uids)), $type);
}
} // end not type == deleted
}
}
public function doAssignment($distributeMethod, $ieid, $folder, $uids, $users)
{
global $app_strings;
$users = explode(",", $users);
$emailIds = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
$out = "";
if ($folder != 'sugar::Emails') {
$emailIds = array();
$uids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
$ie = BeanFactory::newBean('InboundEmail');
$ie->retrieve($ieid);
$messageIndex = 1;
// dealing with an inbound email data so we need to import an email and then
foreach ($uids as $uid) {
$ie->mailbox = $folder;
$ie->connectMailserver();
$msgNo = $uid;
if (!$ie->isPop3Protocol()) {
$msgNo = $ie->getImap()->getMessageNo($uid);
} else {
$msgNo = $ie->getCorrectMessageNoForPop3($uid);
}
if (!empty($msgNo)) {
if ($ie->importOneEmail($msgNo, $uid)) {
$emailIds[] = $ie->email->id;
$ie->deleteMessageOnMailServer($uid);
//$ie->retrieve($ieid);
//$ie->connectMailserver();
$ie->mailbox = $folder;
$ie->deleteMessageFromCache(($uids[] = $uid));
} else {
$out = $out . "Message No : " . $messageIndex . " failed. Reason : Message already imported \r\n";
}
}
$messageIndex++;
} // for
} // if
if (count($emailIds) > 0) {
$this->doDistributionWithMethod($users, $emailIds, $distributeMethod);
} // if
return $out;
} // fn
/**
* get team id and team set id from request
* @return array
*/
public function getTeams()
{
}
public function doDistributionWithMethod($users, $emailIds, $distributionMethod)
{
// we have users and the items to distribute
if ($distributionMethod == 'roundRobin') {
$this->distRoundRobin($users, $emailIds);
} elseif ($distributionMethod == 'leastBusy') {
$this->distLeastBusy($users, $emailIds);
} elseif ($distributionMethod == 'direct') {
if (count($users) > 1) {
// only 1 user allowed in direct assignment
$error = 1;
} else {
$user = $users[0];
$this->distDirect($user, $emailIds);
} // else
} // elseif
} // fn
/**
* distributes emails to users on Round Robin basis
* @param $userIds array of users to dist to
* @param $mailIds array of email ids to push on those users
* @return boolean true on success
*/
public function distRoundRobin($userIds, $mailIds)
{
// check if we have a 'lastRobin'
$lastRobin = $userIds[0];
foreach ($mailIds as $k => $mailId) {
$userIdsKeys = array_flip($userIds); // now keys are values
$thisRobinKey = $userIdsKeys[$lastRobin] + 1;
if (!empty($userIds[$thisRobinKey])) {
$thisRobin = $userIds[$thisRobinKey];
$lastRobin = $userIds[$thisRobinKey];
} else {
$thisRobin = $userIds[0];
$lastRobin = $userIds[0];
}
$email = BeanFactory::newBean('Emails');
$email->retrieve($mailId);
$email->assigned_user_id = $thisRobin;
$email->status = 'unread';
$email->save();
}
return true;
}
/**
* distributes emails to users on Least Busy basis
* @param $userIds array of users to dist to
* @param $mailIds array of email ids to push on those users
* @return boolean true on success
*/
public function distLeastBusy($userIds, $mailIds)
{
foreach ($mailIds as $k => $mailId) {
$email = BeanFactory::newBean('Emails');
$email->retrieve($mailId);
foreach ($userIds as $k => $id) {
$r = $this->db->query("SELECT count(*) AS c FROM emails WHERE assigned_user_id = '.$id.' AND status = 'unread'");
$a = $this->db->fetchByAssoc($r);
$counts[$id] = $a['c'];
}
asort($counts); // lowest to highest
$countsKeys = array_flip($counts); // keys now the 'count of items'
$leastBusy = array_shift($countsKeys); // user id of lowest item count
$email->assigned_user_id = $leastBusy;
$email->status = 'unread';
$email->save();
}
return true;
}
/**
* distributes emails to 1 user
* @param $user users to dist to
* @param $mailIds array of email ids to push
* @return boolean true on success
*/
public function distDirect($user, $mailIds)
{
foreach ($mailIds as $k => $mailId) {
$email = BeanFactory::newBean('Emails');
$email->retrieve($mailId);
$email->assigned_user_id = $user;
$email->status = 'unread';
$email->save();
}
return true;
}
/**
* @param array $userIds
* @return array
*/
public function getAssignedEmailsCountForUsers($userIds)
{
$counts = [];
foreach ($userIds as $id) {
$idQuoted = $this->db->quoted($id);
$r = $this->db->query("SELECT count(*) AS c FROM emails WHERE assigned_user_id = $idQuoted AND status = 'unread'");
$a = $this->db->fetchByAssoc($r);
$counts[$id] = $a['c'];
}
return $counts;
}
public function getLastRobin($ie)
{
$lastRobin = "";
if ($this->validCacheFileExists($ie->id, 'folders', "robin.cache.php")) {
$lastRobin = $this->getCacheValue($ie->id, 'folders', "robin.cache.php", 'robin');
} // if
return $lastRobin;
} // fn
public function setLastRobin($ie, $lastRobin)
{
global $sugar_config;
$cacheFolderPath = sugar_cached("modules/Emails/{$ie->id}/folders");
if (!file_exists($cacheFolderPath)) {
mkdir_recursive($cacheFolderPath);
}
$this->writeCacheFile('robin', $lastRobin, $ie->id, 'folders', "robin.cache.php");
} // fn
/**
* returns the metadata defining a single email message for display. Uses cache file if it exists
* @return array
*/
public function getSingleMessage($ie)
{
global $timedate;
global $app_strings, $mod_strings;
$ie->retrieve($_REQUEST['ieId']);
$noCache = true;
$ie->mailbox = $_REQUEST['mbox'];
$filename = $_REQUEST['mbox'] . $_REQUEST['uid'] . ".php";
$md5uidl = "";
if ($ie->isPop3Protocol()) {
$md5uidl = md5($_REQUEST['uid']);
$filename = $_REQUEST['mbox'] . $md5uidl . ".php";
} // if
if (isset($filename) && strpos($filename, "..") !== false) {
die("Directory navigation attack denied.");
}
if ($this->validCacheFileExists($_REQUEST['ieId'], 'messages', $filename)) {
$out = $this->getCacheValue($_REQUEST['ieId'], 'messages', $filename, 'out');
$noCache = false;
// something fubar'd the cache?
if (empty($out['meta']['email']['name']) && empty($out['meta']['email']['description'])) {
$noCache = true;
} else {
// When sending data from cache, convert date into users preffered format
$dateTimeInGMTFormat = $out['meta']['email']['date_start'];
$out['meta']['email']['date_start'] = $timedate->to_display_date_time($dateTimeInGMTFormat);
} // else
}
if ($noCache) {
$writeToCacheFile = true;
if ($ie->isPop3Protocol()) {
$status = $ie->setEmailForDisplay($_REQUEST['uid'], true, true, true);
} else {
$status = $ie->setEmailForDisplay($_REQUEST['uid'], false, true, true);
}
$out = $ie->displayOneEmail($_REQUEST['uid'], $_REQUEST['mbox']);
// modify the out object to store date in GMT format on the local cache file
$dateTimeInUserFormat = $out['meta']['email']['date_start'];
$out['meta']['email']['date_start'] = $timedate->to_db($dateTimeInUserFormat);
if ($status == 'error') {
$writeToCacheFile = false;
}
if ($writeToCacheFile) {
if ($ie->isPop3Protocol()) {
$this->writeCacheFile(
'out',
$out,
$_REQUEST['ieId'],
'messages',
"{$_REQUEST['mbox']}{$md5uidl}.php"
);
} else {
$this->writeCacheFile(
'out',
$out,
$_REQUEST['ieId'],
'messages',
"{$_REQUEST['mbox']}{$_REQUEST['uid']}.php"
);
} // else
// restore date in the users preferred format to be send on to UI for diaply
$out['meta']['email']['date_start'] = $dateTimeInUserFormat;
} // if
}
$out['meta']['email']['toaddrs'] = $this->generateExpandableAddrs($out['meta']['email']['toaddrs']);
if (!empty($out['meta']['email']['cc_addrs'])) {
$ccs = $this->generateExpandableAddrs($out['meta']['email']['cc_addrs']);
$out['meta']['cc'] = <<<eoq
<tr>
<td NOWRAP valign="top" class="displayEmailLabel">
{$app_strings['LBL_EMAIL_CC']}:
</td>
<td class="displayEmailValue">
{$ccs}
</td>
</tr>
eoq;
}
if (empty($out['meta']['email']['description'])) {
$out['meta']['email']['description'] = $mod_strings['LBL_EMPTY_EMAIL_BODY'];
}
if ($noCache) {
$GLOBALS['log']->debug("EMAILUI: getSingleMessage() NOT using cache file");
} else {
$GLOBALS['log']->debug("EMAILUI: getSingleMessage() using cache file [ " . $_REQUEST['mbox'] . $_REQUEST['uid'] . ".php ]");
}
$this->setReadFlag($_REQUEST['ieId'], $_REQUEST['mbox'], $_REQUEST['uid']);
return $out;
}
/**
* Returns the HTML for a list of emails in a given folder
* @param GUID $ieId GUID to InboundEmail instance
* @param string $mbox Mailbox path name in dot notation
* @param int $folderListCacheOffset Seconds for valid cache file
* @return string HTML render of list.
*/
public function getListEmails($ieId, $mbox, $folderListCacheOffset, $forceRefresh = 'false')
{
global $sugar_config;
$ie = BeanFactory::newBean('InboundEmail');
$ie->retrieve($ieId);
$list = $ie->displayFolderContents($mbox, $forceRefresh);
return $list;
}
/**
* Returns the templatized compose screen. Used by reply, forwards and draft status messages.
* @param object email Email bean in focus
*/
public function displayComposeEmail($email)
{
global $locale;
global $current_user;
$ea = new SugarEmailAddress();
if (!empty($email)) {
$email->cids2Links();
$description = (empty($email->description_html)) ? $email->description : $email->description_html;
}
//Get the most complete address list availible for this email
$addresses = array('toAddresses' => 'to', 'ccAddresses' => 'cc', 'bccAddresses' => 'bcc');
foreach ($addresses as $var => $type) {
$$var = "";
foreach (array("{$type}_addrs_names", "{$type}addrs", "{$type}_addrs") as $emailVar) {
if (!empty($email->$emailVar)) {
$$var = $email->$emailVar;
break;
}
}
}
$ret = array();
$ret['type'] = $email->type;
$ret['name'] = $email->name;
$ret['description'] = $description;
$ret['from'] = (isset($_REQUEST['composeType']) && $_REQUEST['composeType'] == 'forward') ? "" : $email->from_addr;
isValidEmailAddress($ret['from']);
$ret['to'] = from_html($toAddresses);
$ret['uid'] = $email->id;
$ret['parent_name'] = $email->parent_name;
$ret['parent_type'] = $email->parent_type;
$ret['parent_id'] = $email->parent_id;
if ($email->type == 'draft') {
$ret['cc'] = from_html($ccAddresses);
$ret['bcc'] = $bccAddresses;
}
// reply all
if (isset($_REQUEST['composeType']) && $_REQUEST['composeType'] == 'replyAll') {
$ret['cc'] = from_html($ccAddresses);
$ret['bcc'] = $bccAddresses;
$userEmails = array();
$userEmailsMeta = $ea->getAddressesByGUID($current_user->id, 'Users');
foreach ($userEmailsMeta as $emailMeta) {
$userEmails[] = from_html(strtolower(trim($emailMeta['email_address'])));
}
$fromAddr = from_html(strtolower(trim($email->from_addr)));
isValidEmailAddress($fromAddr);
$userEmails[] = $fromAddr;
$ret['cc'] = from_html($email->cc_addrs);
$toAddresses = from_html($toAddresses);
$to = str_replace($this->addressSeparators, "::", $toAddresses);
$exTo = explode("::", $to);
if (is_array($exTo)) {
foreach ($exTo as $addr) {
$addr = strtolower(trim($addr));
if (!in_array($addr, $userEmails)) {
if (!empty($ret['cc'])) {
$ret['cc'] = $ret['cc'] . ", ";
}
$ret['cc'] = $ret['cc'] . trim($addr);
}
}
} elseif (!empty($exTo)) {
$exTo = trim($exTo);
if (!in_array($exTo, $userEmails)) {
$ret['cc'] = $ret['cc'] . ", " . $exTo;
}
}
}
return $ret;
}
/**
* Formats email body on reply/forward
* @param object email Email object in focus
* @param string type
* @return object email
*/
public function handleReplyType($email, $type)
{
global $mod_strings;
$GLOBALS['log']->debug("****At Handle Reply Type: $type");
switch ($type) {
case "reply":
case "replyAll":
$header = $email->getReplyHeader();
if (!preg_match('/^(re:)+/i', $email->name)) {
$email->name = "{$mod_strings['LBL_RE']} {$email->name}";
}
if ($type == "reply") {
$email->cc_addrs = "";
if (!empty($email->reply_to_addr)) {
$email->from_addr = $email->reply_to_addr;
isValidEmailAddress($email->from_addr);
} // if
} else {
if (!empty($email->reply_to_addr)) {
$email->to_addrs = $email->to_addrs . "," . $email->reply_to_addr;
} // if
} // else
break;
case "forward":
$header = $email->getForwardHeader();
if (!preg_match('/^(fw:)+/i', $email->name)) {
$email->name = "{$mod_strings['LBL_FW']} {$email->name}";
}
$email->cc_addrs = "";
break;
case "replyCase":
$GLOBALS['log']->debug("EMAILUI: At reply case");
$header = $email->getReplyHeader();
$myCase = BeanFactory::newBean('Cases');
$myCase->retrieve($email->parent_id);
$myCaseMacro = $myCase->getEmailSubjectMacro();
$email->parent_name = $myCase->name;
$GLOBALS['log']->debug("****Case # : {$myCase->case_number} macro: $myCaseMacro");
if (!strpos($email->name, str_replace('%1', $myCase->case_number, $myCaseMacro))) {
$GLOBALS['log']->debug("Replacing");
$email->name = str_replace('%1', $myCase->case_number, $myCaseMacro) . ' ' . $email->name;
}
$email->name = "{$mod_strings['LBL_RE']} {$email->name}";
break;
}
$html = trim($email->description_html);
$plain = trim($email->description);
$desc = (!empty($html)) ? $html : $plain;
$email->description = $header . $email->quoteHtmlEmailForNewEmailUI($desc);
return $email;
}
///////////////////////////////////////////////////////////////////////////
//// PRIVATE HELPERS
/**
* Generates a UNION query to get one list of users, contacts, leads, and
* prospects; used specifically for the addressBook
*/
public function _getPeopleUnionQuery($whereArr, $person)
{
global $current_user, $app_strings;
$db = DBManagerFactory::getInstance();
if (!isset($person) || $person === 'LBL_DROPDOWN_LIST_ALL') {
$peopleTables = array(
"users",
"contacts",
"leads",
"prospects",
"accounts"
);
} else {
$peopleTables = array($person);
}
$q = '';
$whereAdd = "";
foreach ($whereArr as $column => $clause) {
if (!empty($whereAdd)) {
$whereAdd .= " AND ";
}
$clause = $current_user->db->quote($clause);
$whereAdd .= "{$column} LIKE '{$clause}%'";
}
foreach ($peopleTables as $tableName) {
$module = ucfirst($tableName);
$personBean = BeanFactory::getBean($module);
if ($personBean !== false || !$personBean->ACLAccess('list')) {
continue;
} // if
$table = $personBean->getTableName();
$where = "({$table}.deleted = 0 AND eabr.primary_address = 1 AND {$table}.id <> '{$current_user->id}')";
$accessWhere = $personBean->buildAccessWhere('list');
if (!empty($accessWhere)) {
$where .= ' AND '. $accessWhere;
}
if (!empty($whereAdd)) {
$where .= " AND ({$whereAdd})";
}
if ($personBean instanceof Company) {
$t = "SELECT {$table}.id, '' first_name, {$table}.name, eabr.primary_address, ea.email_address, '{$module}' module ";
} else {
$t = "SELECT {$table}.id, {$table}.first_name, {$table}.last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
}
$t .= "FROM {$table} ";
$t .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
$t .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
$t .= " WHERE {$where}";
if (!empty($q)) {
$q .= "\n UNION ALL \n";
}
$q .= "({$t})";
}
$countq = "SELECT count(people.id) c from ($q) people";
$q .= "ORDER BY last_name";
return array('query' => $q, 'countQuery' => $countq);
}
/**
* get emails of related bean for a given bean id
* @param $beanType
* @param $condition array of conditions inclued bean id
* @return array('query' => $q, 'countQuery' => $countq);
*/
public function getRelatedEmail($beanType, $whereArr, $relatedBeanInfoArr = '')
{
global $beanList, $current_user, $app_strings;
$finalQuery = '';
$searchBeans = null;
if ($beanType === 'LBL_DROPDOWN_LIST_ALL') {
$searchBeans = array(
"users",
"contacts",
"leads",
"prospects",
"accounts"
);
}
if ($relatedBeanInfoArr == '' || empty($relatedBeanInfoArr['related_bean_type'])) {
if ($searchBeans != null) {
$q = array();
foreach ($searchBeans as $searchBean) {
$searchq = $this->findEmailFromBeanIds('', $searchBean, $whereArr);
if (!empty($searchq)) {
$q[] = "($searchq)";
}
}
if (!empty($q)) {
$finalQuery .= implode("\n UNION ALL \n", $q);
}
} else {
$finalQuery = $this->findEmailFromBeanIds('', $beanType, $whereArr);
}
} else {
$class = $beanList[$relatedBeanInfoArr['related_bean_type']];
$focus = new $class();
$focus->retrieve($relatedBeanInfoArr['related_bean_id']);
if ($searchBeans != null) {
$q = array();
foreach ($searchBeans as $searchBean) {
if ($focus->load_relationship($searchBean)) {
$data = $focus->$searchBean->get();
if (count($data) != 0) {
$q[] = '(' . $this->findEmailFromBeanIds($data, $searchBean, $whereArr) . ')';
}
}
}
if (!empty($q)) {
$finalQuery .= implode("\n UNION ALL \n", $q);
}
} else {
if ($focus->load_relationship($beanType)) {
$data = $focus->$beanType->get();
if (count($data) != 0) {
$finalQuery = $this->findEmailFromBeanIds($data, $beanType, $whereArr);
}
}
}
}
$countq = "SELECT count(people.id) c from ($finalQuery) people";
return array('query' => $finalQuery, 'countQuery' => $countq);
}
public function findEmailFromBeanIds($beanIds, $beanType, $whereArr)
{
global $current_user;
$q = '';
$whereAdd = "";
$relatedIDs = '';
if ($beanIds != '') {
foreach ($beanIds as $key => $value) {
$beanIds[$key] = '\'' . $value . '\'';
}
$relatedIDs = implode(',', $beanIds);
}
$module = ucfirst($beanType);
$personBean = BeanFactory::getBean($module);
if ($personBean !== false && $personBean->ACLAccess('list')) {
if ($personBean instanceof Company) {
if (isset($whereArr['first_name'])) {
$whereArr['name'] = $whereArr['first_name'];
}
unset($whereArr['last_name']);
unset($whereArr['first_name']);
}
foreach ($whereArr as $column => $clause) {
if (!empty($whereAdd)) {
$whereAdd .= " OR ";
}
$clause = $current_user->db->quote($clause);
$whereAdd .= "{$column} LIKE '{$clause}%'";
}
$table = $personBean->getTableName();
if ($relatedIDs !== '') {
$where = "({$table}.deleted = 0 AND eabr.primary_address = 1 AND {$table}.id in ($relatedIDs))";
} else {
$where = "({$table}.deleted = 0 AND eabr.primary_address = 1)";
}
$accessWhere = $personBean->buildAccessWhere('list');
if (!empty($accessWhere)) {
$where .= ' AND '. $accessWhere;
}
if (!empty($whereAdd)) {
$where .= " AND ({$whereAdd})";
}
if ($personBean instanceof Company) {
$t = "SELECT {$table}.id, '' first_name, {$table}.name last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
} else {
$t = "SELECT {$table}.id, {$table}.first_name, {$table}.last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
}
$t .= "FROM {$table} ";
$t .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
$t .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
$t .= " WHERE {$where}";
} // if
return $t;
}
/**
* Cleans UID lists
* @param mixed $uids
* @param bool $returnString False will return an array
* @return mixed
*/
public function _cleanUIDList($uids, $returnString = false)
{
global $app_strings;
$GLOBALS['log']->debug("_cleanUIDList: before - [ {$uids} ]");
if (!is_array($uids)) {
$returnString = true;
$exUids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
$uids = $exUids;
}
$cleanUids = array();
foreach ($uids as $uid) {
$cleanUids[$uid] = $uid;
}
sort($cleanUids);
if ($returnString) {
$cleanImplode = implode($app_strings['LBL_EMAIL_DELIMITER'], $cleanUids);
$GLOBALS['log']->debug("_cleanUIDList: after - [ {$cleanImplode} ]");
return $cleanImplode;
}
return $cleanUids;
}
/**
* Creates Mail folder
*
* @param object $user User in focus
* @param array $folder_params Array of parameters for folder creation
*/
protected function createFolder($user, $folder_params)
{
$folder = new SugarFolder();
foreach ($folder_params as $key => $val) {
$folder->$key = $val;
}
$folder->save();
return $folder;
}
/**
* Creates defaults for the User
* @param object $user User in focus
*/
public function preflightUser(&$user)
{
global $mod_strings;
$folder_types = array();
$params = array(
// My Emails
"inbound" => array(
'name' => $mod_strings['LNK_MY_INBOX'],
'folder_type' => "inbound",
'has_child' => 1,
'dynamic_query' => $this->generateDynamicFolderQuery("inbound", $user->id),
'is_dynamic' => 1,
'created_by' => $user->id,
'modified_by' => $user->id,
),
// My Drafts
"draft" => array(
'name' => $mod_strings['LNK_MY_DRAFTS'],
'folder_type' => "draft",
'has_child' => 0,
'dynamic_query' => $this->generateDynamicFolderQuery("draft", $user->id),
'is_dynamic' => 1,
'created_by' => $user->id,
'modified_by' => $user->id,
),
// Sent Emails
"sent" => array(
'name' => $mod_strings['LNK_SENT_EMAIL_LIST'],
'folder_type' => "sent",
'has_child' => 0,
'dynamic_query' => $this->generateDynamicFolderQuery("sent", $user->id),
'is_dynamic' => 1,
'created_by' => $user->id,
'modified_by' => $user->id,
),
// Archived Emails
"archived" => array(
'name' => $mod_strings['LBL_LIST_TITLE_MY_ARCHIVES'],
'folder_type' => "archived",
'has_child' => 0,
'dynamic_query' => '',
'is_dynamic' => 1,
'created_by' => $user->id,
'modified_by' => $user->id,
),
);
$q = "SELECT * FROM folders f WHERE f.created_by = '{$user->id}' AND f.deleted = 0 AND coalesce(" . $user->db->convert(
"f.folder_type",
"length"
) . ",0) > 0";
$r = $user->db->query($q);
while ($row = DBManagerFactory::getInstance()->fetchByAssoc($r)) {
if ($row['folder_type'] == 'inbound') {
$parent_id = $row['id'];
}
if (!in_array($row['folder_type'], $folder_types)) {
array_push($folder_types, $row['folder_type']);
}
if (isset($params[$row['folder_type']])) {
unset($params[$row['folder_type']]);
}
}
require_once("include/SugarFolders/SugarFolders.php");
foreach ($params as $type => $type_params) {
if ($type == "inbound") {
$folder = $this->createFolder($user, $params[$type]);
$parent_id = $folder->id;
// handle the case where inbound folder was deleted, but other folders exist
if (count($folder_types) != 0) {
// This update query will exclude inbound parent, and any custom created folders.
// For others, it will update their parent_id for the current user.
$q = "UPDATE folders SET parent_folder = '" . $parent_id .
"' WHERE folder_type IN ('draft', 'sent', 'archived') AND created_by = '" . $user->id . "'";
$q = "UPDATE folders SET parent_folder = '" . $parent_id .
"' WHERE folder_type IN ('draft', 'sent', 'archived') AND created_by = '" . $user->id . "'";
$r = $user->db->query($q);
}
} else {
$params[$type]['parent_folder'] = $parent_id;
$this->createFolder($user, $params[$type]);
}
}
}
/**
* Parses the core dynamic folder query
* @param string $type 'inbound', 'draft', etc.
* @param string $userId
* @return string
*/
public function generateDynamicFolderQuery($type, $userId)
{
$q = $this->coreDynamicFolderQuery;
$status = $type;
if ($type == "sent") {
$type = "out";
}
$replacee = array("::TYPE::", "::STATUS::", "::USER_ID::");
$replacer = array($type, $status, $userId);
$ret = str_replace($replacee, $replacer, $q);
if ($type == 'inbound') {
$ret .= " AND status NOT IN ('sent', 'archived', 'draft') AND type NOT IN ('out', 'archived', 'draft')";
} else {
$ret .= " AND status NOT IN ('archived') AND type NOT IN ('archived')";
}
return $ret;
}
/**
* Preps the User's cache dir
*/
public function preflightUserCache()
{
$path = clean_path($this->userCacheDir);
if (!file_exists($this->userCacheDir)) {
mkdir_recursive($path);
}
$files = findAllFiles($path, array());
foreach ($files as $file) {
unlink($file);
}
}
public function clearInboundAccountCache($ieId)
{
global $sugar_config;
$cacheRoot = sugar_cached("modules/Emails/{$ieId}");
$files = findAllFiles($cacheRoot . "/messages/", array());
foreach ($files as $file) {
unlink($file);
} // fn
$files = findAllFiles($cacheRoot . "/attachments/", array());
foreach ($files as $file) {
unlink($file);
} // for
} // fn
/**
* returns an array of EmailTemplates that the user has access to for the compose email screen
* @return array
*/
public function getEmailTemplatesArray()
{
global $app_strings;
if (ACLController::checkAccess('EmailTemplates', 'list', true) && ACLController::checkAccess(
'EmailTemplates',
'view',
true
)
) {
$et = BeanFactory::newBean('EmailTemplates');
$etResult = $et->db->query($et->create_new_list_query(
'',
"(email_templates.type IS NULL OR email_templates.type='' OR email_templates.type='email')",
array(),
array(),
''
));
$email_templates_arr = array('' => $app_strings['LBL_NONE']);
while ($etA = $et->db->fetchByAssoc($etResult)) {
$email_templates_arr[$etA['id']] = $etA['name'];
}
} else {
$email_templates_arr = array('' => $app_strings['LBL_NONE']);
}
return $email_templates_arr;
}
public function getFromAccountsArray($ie)
{
global $current_user;
global $app_strings;
$ieAccountsFull = $ie->retrieveAllByGroupIdWithGroupAccounts($current_user->id);
$ieAccountsFrom = array();
$oe = new OutboundEmail();
$system = $oe->getSystemMailerSettings();
$ret = $current_user->getUsersNameAndEmail();
$ret['name'] = from_html($ret['name']);
$useMyAccountString = true;
if (empty($ret['email'])) {
$systemReturn = $current_user->getSystemDefaultNameAndEmail();
$ret['email'] = $systemReturn['email'];
$ret['name'] = from_html($systemReturn['name']);
$useMyAccountString = false;
} // if
$myAccountString = '';
if ($useMyAccountString) {
$myAccountString = " - {$app_strings['LBL_MY_ACCOUNT']}";
} // if
//Check to make sure that the user has set the associated inbound email account -> outbound account is active.
$showFolders = sugar_unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
$sf = new SugarFolder();
$groupSubs = $sf->getSubscriptions($current_user);
foreach ($ieAccountsFull as $k => $v) {
$personalSelected = (!empty($showFolders) && in_array($v->id, $showFolders));
$allowOutboundGroupUsage = isTrue($v->get_stored_options('allow_outbound_group_usage', false) ?? false);
$groupSelected = (in_array($v->groupfolder_id, $groupSubs) && $allowOutboundGroupUsage);
$selected = ($personalSelected || $groupSelected);
if (!$selected) {
$GLOBALS['log']->debug("Inbound Email {$v->name}, not selected and will not be available for selection within compose UI.");
continue;
}
$name = $v->get_stored_options('from_name');
$addr = $v->get_stored_options('from_addr');
isValidEmailAddress($addr);
if ($name != null && $addr != null) {
$name = from_html($name);
if (!$v->is_personal) {
$ieAccountsFrom[] = array(
"value" => $v->id,
"text" => "{$name} ({$addr}) - {$app_strings['LBL_EMAIL_UPPER_CASE_GROUP']}"
);
} else {
$ieAccountsFrom[] = array("value" => $v->id, "text" => "{$name} ({$addr})");
} // else
} // if
} // foreach
$userSystemOverride = $oe->getUsersMailerForSystemOverride($current_user->id);
//Substitute in the users system override if its available.
if ($userSystemOverride != null) {
$system = $userSystemOverride;
}
if (!empty($system->mail_smtpserver)) {
$admin = BeanFactory::newBean('Administration');
$admin->retrieveSettings(); //retrieve all admin settings.
$ieAccountsFrom[] = array(
"value" => $system->id,
"text" =>
"{$ret['name']} ({$ret['email']}){$myAccountString}"
);
}
return $ieAccountsFrom;
} // fn
/**
* This function will return all the accounts this user has access to based on the
* match of the emailId passed in as a parameter
* @deprecate 7.9
* @param unknown_type $ie
* @return unknown
*/
public function getFromAllAccountsArray($ie, $ret)
{
global $current_user;
global $app_strings;
$ret['fromAccounts'] = array();
if (!isset($ret['to']) && !empty($ret['from'])) {
$ret['fromAccounts']['status'] = false;
return $ret;
}
$ieAccountsFull = $ie->retrieveAllByGroupIdWithGroupAccounts($current_user->id);
$foundInPersonalAccounts = false;
$foundInGroupAccounts = false;
$foundInSystemAccounts = false;
//$toArray = array();
if ($ret['type'] == "draft") {
$toArray = explode(",", $ret['from']);
} else {
$toArray = $ie->email->email2ParseAddressesForAddressesOnly($ret['to']);
} // else
foreach ($ieAccountsFull as $k => $v) {
$storedOptions = sugar_unserialize(base64_decode($v->stored_options));
if (array_search_insensitive($storedOptions['from_addr'], $toArray)) {
if ($v->is_personal) {
$foundInPersonalAccounts = true;
break;
} else {
$foundInGroupAccounts = true;
} // else
} // if
} // foreach
$oe = new OutboundEmail();
$system = $oe->getSystemMailerSettings();
$return = $current_user->getUsersNameAndEmail();
$return['name'] = from_html($return['name']);
$useMyAccountString = true;
if (empty($return['email'])) {
$systemReturn = $current_user->getSystemDefaultNameAndEmail();
$return['email'] = $systemReturn['email'];
$return['name'] = from_html($systemReturn['name']);
$useMyAccountString = false;
} // if
$myAccountString = '';
if ($useMyAccountString) {
$myAccountString = " - {$app_strings['LBL_MY_ACCOUNT']}";
} // if
if (!empty($system->id)) {
$admin = BeanFactory::newBean('Administration');
$admin->retrieveSettings(); //retrieve all admin settings.
if (in_array(trim($return['email']), $toArray)) {
$foundInSystemAccounts = true;
} // if
} // if
if (!$foundInPersonalAccounts && !$foundInGroupAccounts && !$foundInSystemAccounts) {
$ret['fromAccounts']['status'] = false;
return $ret;
} // if
$ieAccountsFrom = array();
foreach ($ieAccountsFull as $k => $v) {
$storedOptions = sugar_unserialize(base64_decode($v->stored_options));
$storedOptionsName = from_html($storedOptions['from_name']);
$selected = false;
if (array_search_insensitive($storedOptions['from_addr'], $toArray)) {
//if ($ret['to'] == $storedOptions['from_addr']) {
$selected = true;
} // if
if ($foundInPersonalAccounts) {
if ($v->is_personal) {
$ieAccountsFrom[] = array(
"value" => $v->id,
"selected" => $selected,
"text" => "{$storedOptionsName} ({$storedOptions['from_addr']})"
);
} // if
} else {
$ieAccountsFrom[] = array(
"value" => $v->id,
"selected" => $selected,
"text" => "{$storedOptionsName} ({$storedOptions['from_addr']}) - {$app_strings['LBL_EMAIL_UPPER_CASE_GROUP']}"
);
} // else
} // foreach
if (!empty($system->id)) {
if (!$foundInPersonalAccounts && !$foundInGroupAccounts && $foundInSystemAccounts) {
$ieAccountsFrom[] = array(
"value" => $system->id,
"selected" => true,
"text" =>
"{$return['name']} ({$return['email']}){$myAccountString}"
);
} else {
$ieAccountsFrom[] = array(
"value" => $system->id,
"text" =>
"{$return['name']} ({$return['email']}){$myAccountString}"
);
} // else
} // if
$ret['fromAccounts']['status'] = ($foundInPersonalAccounts || $foundInGroupAccounts || $foundInSystemAccounts) ? true : false;
$ret['fromAccounts']['data'] = $ieAccountsFrom;
return $ret;
} // fn
/**
* takes an array and creates XML
* @param array Array to convert
* @param string Name to wrap highest level items in array
* @return string XML
*/
public function arrayToXML($a, $paramName)
{
if (!is_array($a)) {
return '';
}
$bad = array("<", ">", "'", '"', "&");
$good = array("&lt;", "&gt;", "&#39;", "&quot;", "&amp;");
$ret = "";
for ($i = 0; $i < count($a); $i++) {
$email = $a[$i];
$ret .= "\n<{$paramName}>";
foreach ($email as $k => $v) {
$ret .= "\n\t<{$k}>" . str_replace($bad, $good, $v) . "</{$k}>";
}
$ret .= "\n</{$paramName}>";
}
return $ret;
}
/**
* Re-used option getter for Show Accounts multiselect pane
*/
public function getShowAccountsOptions(&$ie)
{
global $current_user;
global $app_strings;
global $mod_strings;
$ieAccountsFull = $ie->retrieveAllByGroupId($current_user->id);
$ieAccountsShowOptionsMeta = array();
$showFolders = sugar_unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
$defaultIEAccount = $ie->getUsersDefaultOutboundServerId($current_user);
foreach ($ieAccountsFull as $k => $v) {
$default = $defaultIEAccount == $v->id;
$has_groupfolder = !empty($v->groupfolder_id);
$type = $v->is_personal ? $mod_strings['LBL_MAILBOX_TYPE_PERSONAL'] : $mod_strings['LBL_MAILBOX_TYPE_GROUP'];
$personalSelected = (!empty($showFolders) && in_array($v->id, $showFolders, true));
$allowOutboundGroupUsage = isTrue($v->get_stored_options('allow_outbound_group_usage', false) ?? false);
$selected = $personalSelected || $allowOutboundGroupUsage || is_admin($current_user);
if (!$selected) {
LoggerManager::getLogger()->debug("Inbound Email {$v->name}, not selected and will not be available for selection within compose UI.");
continue;
}
$ieAccountsShowOptionsMeta[] = array(
"id" => $v->id,
"name" => $v->name,
'is_active' => $selected,
'server_url' => $v->server_url,
'is_group' => !$v->is_personal,
'group_id' => $v->group_id,
'is_default' => $default,
'has_groupfolder' => $has_groupfolder,
'type' => $type
);
}
//Retrieve the grou folders
$f = new SugarFolder();
$groupFolders = $f->getGroupFoldersForSettings($current_user);
foreach ($groupFolders as $singleGroup) {
//Retrieve the related IE accounts.
$relatedIEAccounts = $ie->retrieveByGroupFolderId($singleGroup['id']);
if (count($relatedIEAccounts) == 0) {
$server_url = $app_strings['LBL_EMAIL_MULT_GROUP_FOLDER_ACCOUNTS_EMPTY'];
} else {
if (count($relatedIEAccounts) == 1) {
if ($relatedIEAccounts[0]->status != 'Active' || $relatedIEAccounts[0]->mailbox_type == 'bounce') {
continue;
}
$server_url = $relatedIEAccounts[0]->server_url;
} else {
$server_url = $app_strings['LBL_EMAIL_MULT_GROUP_FOLDER_ACCOUNTS'];
}
}
$type = $mod_strings['LBL_MAILBOX_TYPE_GROUP_FOLDER'];
$ieAccountsShowOptionsMeta[] = array(
"id" => $singleGroup['id'],
"name" => $singleGroup['origName'],
'is_active' => $singleGroup['selected'],
'server_url' => $server_url,
'is_group' => true,
'group_id' => $singleGroup['id'],
'is_default' => false,
'has_groupfolder' => true,
'type' => $type
);
}
return $ieAccountsShowOptionsMeta;
}
public function getShowAccountsOptionsForSearch(&$ie)
{
global $current_user;
global $app_strings;
$ieAccountsFull = $ie->retrieveAllByGroupId($current_user->id);
//$ieAccountsShowOptions = "<option value=''>{$app_strings['LBL_NONE']}</option>\n";
$ieAccountsShowOptionsMeta = array();
$ieAccountsShowOptionsMeta[] = array("value" => "", "text" => $app_strings['LBL_NONE'], 'selected' => '');
$showFolders = sugar_unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
foreach ($ieAccountsFull as $k => $v) {
if (!in_array($v->id, $showFolders)) {
continue;
}
$group = (!$v->is_personal) ? $app_strings['LBL_EMAIL_GROUP'] . "." : "";
$ieAccountsShowOptionsMeta[] = array(
"value" => $v->id,
"text" => $group . $v->name,
'protocol' => $v->protocol
);
}
return $ieAccountsShowOptionsMeta;
}
/**
* Formats a display message on successful async call
* @param string $type Type of message to display
*/
public function displaySuccessMessage($type)
{
global $app_strings;
switch ($type) {
case "delete":
$message = $app_strings['LBL_EMAIL_DELETE_SUCCESS'];
break;
default:
$message = "NOOP: invalid type";
break;
}
$this->smarty->assign('app_strings', $app_strings);
$this->smarty->assign('message', $message);
echo $this->smarty->fetch("modules/Emails/templates/successMessage.tpl");
}
/**
* Validates existence and expiration of a cache file
* @param string $ieId
* @param string $type Type of cache file: folders, messages, etc.
* @param string $file The cachefile name
* @param int refreshOffset Refresh time in secs.
* @return mixed.
*/
public function validCacheFileExists($ieId, $type, $file, $refreshOffset = -1)
{
global $sugar_config;
if ($refreshOffset == -1) {
$refreshOffset = $this->cacheTimeouts[$type]; // use defaults
}
$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
if (file_exists($cacheFilePath)) {
return true;
}
return false;
}
/**
* retrieves the cached value
* @param string $ieId
* @param string $type Type of cache file: folders, messages, etc.
* @param string $file The cachefile name
* @param string $key name of cache value
* @return mixed
*/
public function getCacheValue($ieId, $type, $file, $key)
{
global $sugar_config;
$cleanIeId = cleanDirName($ieId);
$cleanType = cleanDirName($type);
$cleanFile = cleanFileName($file);
$cacheFilePath = sugar_cached("modules/Emails/{$cleanIeId}/{$cleanType}/{$cleanFile}");
$cacheFile = array();
if (file_exists($cacheFilePath)) {
include($cacheFilePath); // provides $cacheFile
if (isset($cacheFile[$key])) {
$ret = unserialize($cacheFile[$key]);
return $ret;
}
} else {
$GLOBALS['log']->debug("EMAILUI: cache file not found [ {$cacheFilePath} ] - creating blank cache file");
$this->writeCacheFile('retArr', array(), $ieId, $type, $file);
}
return null;
}
/**
* retrieves the cache file last touched time
* @param string $ieId
* @param string $type Type of cache file: folders, messages, etc.
* @param string $file The cachefile name
* @return string
*/
public function getCacheTimestamp($ieId, $type, $file)
{
global $sugar_config;
$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
$cacheFile = array();
if (file_exists($cacheFilePath)) {
include($cacheFilePath); // provides $cacheFile['timestamp']
if (isset($cacheFile['timestamp'])) {
$GLOBALS['log']->debug("EMAILUI: found timestamp [ {$cacheFile['timestamp']} ]");
return $cacheFile['timestamp'];
}
}
return '';
}
/**
* Updates the timestamp for a cache file - usually to mark a "check email"
* process
* @param string $ieId
* @param string $type Type of cache file: folders, messages, etc.
* @param string $file The cachefile name
*/
public function setCacheTimestamp($ieId, $type, $file)
{
global $sugar_config;
$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
$cacheFile = array();
if (file_exists($cacheFilePath)) {
include($cacheFilePath); // provides $cacheFile['timestamp']
if (isset($cacheFile['timestamp'])) {
$cacheFile['timestamp'] = strtotime('now');
$GLOBALS['log']->debug("EMAILUI: setting updated timestamp [ {$cacheFile['timestamp']} ]");
return $this->_writeCacheFile($cacheFile, $cacheFilePath);
}
}
}
/**
* Writes caches to flat file in cache dir.
* @param string $key Key to the main cache entry (not timestamp)
* @param mixed $var Variable to be cached
* @param string $ieId I-E focus ID
* @param string $type Folder in cache
* @param string $file Cache file name
*/
public function writeCacheFile($key, $var, $ieId, $type, $file)
{
global $sugar_config;
$cleanIeId = cleanDirName($ieId);
$cleanType = cleanDirName($type);
$cleanFile = cleanFileName($file);
$the_file = sugar_cached("modules/Emails/{$cleanIeId}/{$cleanType}/{$cleanFile}");
$timestamp = strtotime('now');
$array = array();
$array['timestamp'] = $timestamp;
$array[$key] = serialize($var); // serialized since varexport_helper() can't handle PHP objects
return $this->_writeCacheFile($array, $the_file);
}
/**
* Performs the actual file write. Abstracted from writeCacheFile() for
* flexibility
* @param array $array The array to write to the cache
* @param string $file Full path (relative) with cache file name
* @return bool
*/
public function _writeCacheFile($array, $file)
{
global $sugar_config;
$arrayString = var_export_helper($array);
$date = date("r");
$the_string = <<<eoq
<?php // created: {$date}
\$cacheFile = {$arrayString};
?>
eoq;
if ($fh = @sugar_fopen($file, "w")) {
fwrite($fh, $the_string);
fclose($fh);
return true;
} else {
$GLOBALS['log']->debug("EMAILUI: Could not write cache file [ {$file} ]");
return false;
}
}
/**
* Generate JSON encoded data to be consumed by yui datatable.
*
* @param array $data
* @param string $resultsParam The resultsList name
* @return string
*/
public function jsonOuput($data, $resultsParam, $count = 0, $fromCache = true, $unread = -1)
{
global $app_strings;
$count = ($count > 0) ? $count : 0;
if (isset($a['fromCache'])) {
$cached = ($a['fromCache'] == 1) ? 1 : 0;
} else {
$cached = ($fromCache) ? 1 : 0;
}
if ($data['mbox'] == 'undefined' || empty($data['mbox'])) {
$data['mbox'] = $app_strings['LBL_NONE'];
}
$jsonOut = array(
'TotalCount' => $count,
'FromCache' => $cached,
'UnreadCount' => $unread,
$resultsParam => $data['out']
);
return json_encode($jsonOut);
}
/**
* generates XML output from an array
* @param array
* @param string master list Item
* @return string
*/
public function xmlOutput($a, $paramName, $count = 0, $fromCache = true, $unread = -1)
{
global $app_strings;
$count = ($count > 0) ? $count : 0;
if (isset($a['fromCache'])) {
$cached = ($a['fromCache'] == 1) ? 1 : 0;
} else {
$cached = ($fromCache) ? 1 : 0;
}
if ($a['mbox'] == 'undefined' || empty($a['mbox'])) {
$a['mbox'] = $app_strings['LBL_NONE'];
}
$xml = $this->arrayToXML($a['out'], $paramName);
$ret = <<<eoq
<?xml version="1.0" encoding="UTF-8"?>
<EmailPage>
<TotalCount>{$count}</TotalCount>
<UnreadCount>{$unread}</UnreadCount>
<FromCache> {$cached} </FromCache>
<{$paramName}s>
{$xml}
</{$paramName}s>
</EmailPage>
eoq;
return $ret;
}
/**
* Generate to/cc addresses string in email detailview.
*
* @param string $str
* @param string $target values: to, cc
* @param int $defaultNum
* @return string $str
*/
public function generateExpandableAddrs($str)
{
global $mod_strings;
$tempStr = $str . ',';
$tempStr = html_entity_decode($tempStr);
$tempStr = $this->unifyEmailString($tempStr);
$defaultNum = 2;
$pattern = '/@.*,/U';
preg_match_all($pattern, $tempStr, $matchs);
$totalCount = count($matchs[0]);
if (!empty($matchs[0]) && $totalCount > $defaultNum) {
$position = strpos($tempStr, $matchs[0][$defaultNum]);
$hiddenCount = $totalCount - $defaultNum;
$frontStr = substr($tempStr, 0, $position);
$backStr = substr($tempStr, $position, -1);
$str = htmlentities($frontStr) . '<a class="utilsLink" onclick="SUGAR.email2.detailView.displayAllAddrs(this);">...[' . $mod_strings['LBL_EMAIL_DETAIL_VIEW_SHOW'] . $hiddenCount . $mod_strings['LBL_EMAIL_DETAIL_VIEW_MORE'] . ']</a><span style="display: none;">' . htmlentities($backStr) . '</span>';
}
return $str;
}
/**
* Unify the separator as ,
*
* @param String $str email address string
* @return String converted string
*/
public function unifyEmailString($str)
{
preg_match_all('/@.*;/U', $str, $matches);
if (!empty($matches[0])) {
foreach ($matches[0] as $key => $value) {
$new[] = str_replace(";", ",", $value);
}
return str_replace($matches[0], $new, $str);
}
return $str;
}
} // end class def