Merge next into suite 8

This commit is contained in:
Jack Anderson 2025-08-05 23:46:27 +01:00
commit ced5a7deee
121 changed files with 3149 additions and 2014 deletions

View file

@ -1,7 +1,7 @@
<?php
// Swagger needs this, but should remove - CORS
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Methods: POST, PATCH, GET, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
// @codingStandardsIgnoreStart

View file

@ -76,8 +76,9 @@ class MetaService
'default',
'len',
'precision',
'comments',
'comment',
'required',
'vname'
];
/**

View file

@ -90,7 +90,7 @@ class UserPreferencesService
$preferences = [];
while ($row = $db->fetchByAssoc($result)) {
$category = $row['category'];
$preferences[$category] = unserialize(base64_decode($row['contents']));
$preferences[$category] = unserialize(base64_decode($row['contents']), ['allowed_classes' => false]);
}
$dataResponse = new DataResponse('UserPreference', $params->getUserId());

View file

@ -916,7 +916,7 @@ class PackageManager
$installed->manifest = base64_encode(serialize($serial_manifest));
$installed->save();
} else {
$serial_manifest = unserialize(base64_decode($installed->manifest));
$serial_manifest = unserialize(base64_decode($installed->manifest), ['allowed_classes' => false]);
$manifest = $serial_manifest['manifest'];
}
if (($upgrades_installed==0 || $uh->UninstallAvailable($installeds, $installed))

View file

@ -2,7 +2,7 @@
<img width="180px" height="41px" src="https://suitecrm.com/wp-content/uploads/2017/12/logo.png" align="right" />
</a>
# SuiteCRM 7.14.6
# SuiteCRM 7.14.7
[![Build Status](https://travis-ci.org/salesagility/SuiteCRM.svg?branch=hotfix)](https://travis-ci.org/salesagility/SuiteCRM)
[![codecov](https://codecov.io/gh/salesagility/SuiteCRM/branch/hotfix/graph/badge.svg)](https://codecov.io/gh/salesagility/SuiteCRM/branch/hotfix)

View file

@ -1,20 +1,19 @@
{
"name": "salesagility/suitecrm",
"name": "suitecrm/suitecrm",
"description": "SuiteCRM",
"homepage": "https://suitecrm.com",
"type": "project",
"license": "GPL-3.0",
"authors": [
{
"name": "SalesAgility Ltd"
"name": "SuiteCRM Ltd"
}
],
"support": {
"issues": "https://github.com/salesagility/SuiteCRM/issues",
"issues": "https://github.com/suitecrm/SuiteCRM/issues",
"wiki": "https://docs.suitecrm.com",
"forum": "https://community.suitecrm.com",
"chat": "https://gitter.im/suitecrm/Lobby",
"source": "https://github.com/salesagility/SuiteCRM"
"source": "https://github.com/suitecrm/SuiteCRM"
},
"config": {
"vendor-dir": "vendor",
@ -39,10 +38,11 @@
"ext-json": "*",
"ext-openssl": "*",
"ext-zip": "*",
"ext-intl": "*",
"consolidation/robo": "^3.0",
"elasticsearch/elasticsearch": "^7.13",
"ezyang/htmlpurifier": "^4.10",
"google/apiclient": "^2.7",
"google/apiclient": "^2.14",
"google/recaptcha": "^1.1",
"gymadarasz/ace": "^1.2",
"gymadarasz/imagesloaded": "^4.1",
@ -60,13 +60,12 @@
"slim/slim": "^3.8",
"smarty/smarty": "^4",
"soundasleep/html2text": "~0.5",
"symfony/options-resolver": "^3.4",
"symfony/validator": "^3.4",
"symfony/options-resolver": "^5.4",
"symfony/validator": "^5.4",
"symfony/yaml": "^5.2",
"tecnickcom/tcpdf": "^6.4",
"tedivm/jshrink": "^1.3",
"tinymce/tinymce": "^5.10",
"vlucas/phpdotenv": "^3.5",
"voku/anti-xss": "^4.0",
"wikimedia/composer-merge-plugin": "^2.0",
"zbateson/mail-mime-parser": "^2.2",
@ -74,8 +73,7 @@
"zf1/zend-loader": "^1.12",
"zf1/zend-oauth": "^1.12",
"zf1/zend-registry": "^1.12",
"zf1/zend-search-lucene": "^1.12",
"ext-intl": "*"
"zf1/zend-search-lucene": "^1.12"
},
"require-dev": {
"browserstack/browserstack-local": "^1.1",
@ -95,7 +93,8 @@
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.16.0",
"scssphp/scssphp": "^1.5"
"scssphp/scssphp": "^1.5",
"vlucas/phpdotenv": "^5.5"
},
"autoload": {
"files": [

File diff suppressed because it is too large Load diff

View file

@ -57,7 +57,7 @@ if (!is_windows()) {
$cronUser = getRunningUser();
if ($cronUser == '') {
$GLOBALS['log']->warning('cron.php: can\'t determine running user. No cron user checks will occur.');
$GLOBALS['log']->warn('cron.php: can\'t determine running user. No cron user checks will occur.');
} elseif (array_key_exists('cron', $sugar_config) && array_key_exists('allowed_cron_users', $sugar_config['cron'])) {
if (!in_array($cronUser, $sugar_config['cron']['allowed_cron_users'])) {
$GLOBALS['log']->fatal("cron.php: running as $cronUser is not allowed in allowed_cron_users ".
@ -70,7 +70,7 @@ if (!is_windows()) {
sugar_die('cron.php running with user that is not in allowed_cron_users in config.php');
}
} else {
$GLOBALS['log']->warning('cron.php: missing expected allowed_cron_users entry in config.php. ' .
$GLOBALS['log']->warn('cron.php: missing expected allowed_cron_users entry in config.php. ' .
'No cron user checks will occur.');
}
}

View file

@ -1012,7 +1012,11 @@ class SugarController
require_once($this->entry_point_registry[$entryPoint]['file']);
$this->_processed = true;
$this->view = '';
} else {
$this->no_action();
}
} elseif (isset($_REQUEST['entryPoint'])) {
$this->no_action();
}
}

View file

@ -57,6 +57,7 @@ $action_view_map['modulelistmenu']= 'modulelistmenu';
$action_view_map['favorites']= 'favorites';
$action_view_map['ajaxui']= 'ajaxui';
$action_view_map['noaccess']= 'noaccess';
$action_view_map['errors']= 'errors';
// SugarPDF
$action_view_map['sugarpdf']= 'sugarpdf';

View file

@ -572,7 +572,7 @@ class SugarApplication
if ($dieIfInvalid) {
header("Cache-Control: no-cache, must-revalidate");
$ss = new Sugar_Smarty;
$ss->assign('host', $http_host[0]);
$ss->assign('host', securexss($http_host[0]));
$ss->assign('action', $this->controller->action);
$ss->assign('whiteListString', $whiteListString);
$ss->display('include/MVC/View/tpls/xsrf.tpl');
@ -591,7 +591,7 @@ class SugarApplication
$whiteListString = "'" . implode("', '", $whiteListActions) . "'";
$ss = new Sugar_Smarty;
$ss->assign('host', $http_ref['host']);
$ss->assign('host', securexss($http_ref['host']));
$ss->assign('action', $this->controller->action);
$ss->assign('whiteListString', $whiteListString);
$ss->display('include/MVC/View/tpls/xsrf.tpl');

View file

@ -0,0 +1,39 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SuiteCRM Ltd.
* Copyright (C) 2025 SuiteCRM 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 SUITECRM, SUITECRM 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/>.
*
* 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
* "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 "Supercharged by SuiteCRM".
*/
class ViewErrors extends SugarView
{
public $type = 'errors';
/**
* @see SugarView::display()
*/
public function display()
{
}
}

View file

@ -89,6 +89,8 @@ class OutboundEmail
public $mail_smtpssl; // bool
public $mail_smtpdisplay; // calculated value, not in DB
public $new_with_id = false;
public $auth_type; // no_auth, oauth2, external_oauth
public $external_oauth_connection_id; // no_auth, oauth2, external_oauth
/**
* Sole constructor
@ -389,6 +391,19 @@ class OutboundEmail
return $allowAccess;
}
public function getSystemEmail(): ?object
{
$q = "SELECT id FROM outbound_email WHERE type = 'system' AND deleted = 0";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
if (!empty($a)) {
return $this->retrieve($a['id']) ?? null;
}
return null;
}
/**
* Retrieves the system's Outbound options
*/

View file

@ -187,7 +187,7 @@ class SearchForm
$this->th->ss->assign('action', $this->action);
$this->th->ss->assign('displayView', $this->displayView);
$this->th->ss->assign('viewTab', $this->getViewTab());
$this->th->ss->assign('user_options', get_user_array(false)); // Fix https://github.com/salesagility/SuiteCRM/issues/10264
$this->th->ss->assign('user_options', get_user_array(false));
require_once('modules/MySettings/StoreQuery.php');
$storeQuery = new StoreQuery();

View file

@ -868,8 +868,6 @@ class SubPanelDefinitions
*/
public function get_hidden_subpanels()
{
global $moduleList;
//create variable as static to minimize queries
static $hidden_subpanels = null;
@ -884,25 +882,9 @@ class SubPanelDefinitions
$hidden_subpanels = $administration->settings['MySettings_hide_subpanels'];
$hidden_subpanels = trim($hidden_subpanels);
//make sure serialized string is not empty
if (!empty($hidden_subpanels)) {
//decode and unserialize to retrieve the array
$hidden_subpanels = base64_decode($hidden_subpanels);
$hidden_subpanels = unserialize($hidden_subpanels);
//Ensure modules saved in the preferences exist.
//get user preference
//unserialize and add to array if not empty
$pref_hidden = array();
foreach ($pref_hidden as $id => $pref_hidden_panel) {
$hidden_subpanels[] = $pref_hidden_panel;
}
} else {
//no settings found, return empty
return $hidden_subpanels;
$hidden_subpanels = unserialize(base64_decode($hidden_subpanels), ['allowed_classes' => false]);
}
} else { //no settings found, return empty
return $hidden_subpanels;
}
}

View file

@ -135,7 +135,7 @@ class SugarCacheFile extends SugarCacheAbstract
) {
// load up the external cache file
if (is_file($cachedfile = sugar_cached($this->_cacheFileName))) {
$this->localCache = unserialize(file_get_contents($cachedfile));
$this->localCache = unserialize(file_get_contents($cachedfile), ['allowed_classes' => false]);
}
if (isset($this->_localStore[$key])) {

View file

@ -141,7 +141,7 @@ class SugarCacheRedis extends SugarCacheAbstract
}
return is_string($returnValue) ?
unserialize($returnValue) :
unserialize($returnValue, ['allowed_classes' => false]) :
$returnValue;
}

View file

@ -86,7 +86,7 @@ class SugarCacheZend extends SugarCacheAbstract
return null;
}
return is_string($raw_cache_value) ?
unserialize($raw_cache_value) :
unserialize($raw_cache_value, ['allowed_classes' => false]) :
$raw_cache_value;
}

View file

@ -40,5 +40,4 @@
*}
{{capture name=display_size assign=size}}{{$displayParams.size|default:6}}{{/capture}}
{html_options name='{{$vardef.name}}[]' options=$user_options size="{{$size}}" style="width: 150px" {{if $size > 1}}multiple="1"{{/if}} selected={{sugarvar key='value' string=true}}}

View file

@ -132,23 +132,28 @@ class SugarPHPMailer extends PHPMailer
$this->Mailer = 'smtp';
$this->Host = $oe->mail_smtpserver;
$this->Port = $oe->mail_smtpport;
if ($oe->mail_smtpssl == 1) {
$this->SMTPSecure = 'ssl';
} // if
if ($oe->mail_smtpssl == 2) {
$this->SMTPSecure = 'tls';
} // if
if ($oe->mail_smtpauth_req) {
$this->SMTPAuth = true;
$this->Username = $oe->mail_smtpuser;
$this->Password = $oe->mail_smtppass;
}
$this->setSecureProtocol($oe->mail_smtpssl);
$this->initSMTPAuth(
$oe->auth_type ?? '',
$oe->external_oauth_connection_id ?? '',
$oe->mail_smtpuser ?? '',
$oe->mail_smtppass ?? '',
);
} else {
$this->Mailer = 'sendmail';
}
}
public function setSystemFromAddress(): void
{
require_once 'include/OutboundEmail/OutboundEmail.php';
$oe = new OutboundEmail();
$oe = $oe->getSystemMailerSettings();
$this->From = $oe->smtp_from_addr ?? '';
$this->FromName = $oe->smtp_from_name ?? '';
}
/**
* Prefills mailer for system
*/
@ -166,22 +171,81 @@ class SugarPHPMailer extends PHPMailer
$this->Mailer = 'smtp';
$this->Host = $oe->mail_smtpserver;
$this->Port = $oe->mail_smtpport;
if ($oe->mail_smtpssl == 1) {
$this->SMTPSecure = 'ssl';
} // if
if ($oe->mail_smtpssl == 2) {
$this->SMTPSecure = 'tls';
} // if
if ($oe->mail_smtpauth_req) {
$this->SMTPAuth = true;
$this->Username = $oe->mail_smtpuser;
$this->Password = $oe->mail_smtppass;
}
$this->setSecureProtocol($oe->mail_smtpssl);
$this->initSMTPAuth(
$oe->auth_type ?? '',
$oe->external_oauth_connection_id ?? '',
$oe->mail_smtpuser ?? '',
$oe->mail_smtppass ?? '',
);
} else {
$this->Mailer = 'sendmail';
}
}
public function initSMTPAuth(
string $authType,
string $externalOAuthConnectionId,
string $smtpUser,
string $smtpPass,
): void {
if ($authType === 'oauth') {
$this->initOAuth(
$authType,
$externalOAuthConnectionId,
$smtpUser,
);
return;
}
if ($authType === 'basic') {
$this->SMTPAuth = true;
$this->Username = $smtpUser;
$this->Password = $smtpPass;
}
}
public function initOAuth(
string $authType,
string $externalOAuthConnectionId,
string $smtpUser
): void
{
if ($authType !== 'oauth') {
return;
}
$this->isSMTP();
$this->SMTPAuth = true;
$this->AuthType = 'XOAUTH2';
$oAuthConnectionId = $externalOAuthConnectionId;
require_once 'modules/ExternalOAuthConnection/services/OAuthAuthorizationService.php';
$oAuth = new OAuthAuthorizationService();
$oAuth->refreshExpiredOAuthToken($oAuthConnectionId);
/** @var ExternalOAuthConnection $oauthConnection */
$oauthConnection = BeanFactory::getBean('ExternalOAuthConnection', $oAuthConnectionId);
$providerId = $oauthConnection->external_oauth_provider_id;
$oauthConfig = new \PHPMailer\PHPMailer\OAuth([
'provider' => $oAuth?->getProvider($providerId)?->getProvider($oauthConnection->client_id, $oauthConnection->client_secret),
'clientId' => $oauthConnection->client_id,
'refreshToken' => $oauthConnection->refresh_token,
'clientSecret' => $oauthConnection->client_secret,
'userName' => $smtpUser
]);
$this->setOAuth(
$oauthConfig
);
}
/**
* handles Charset translation for all visual parts of the email.
*/
@ -469,7 +533,7 @@ eoq;
$ret = parent::send();
$this->exceptions = $saveExceptionsState;
} catch (Exception $e) {
$phpMailerExceptionMsg=$e->errorMessage(); //Pretty error messages from PHPMailer
$phpMailerExceptionMsg =$e->getMessage(); //Pretty error messages from PHPMailer
if ($phpMailerExceptionMsg) {
$GLOBALS['log']->error("send: PHPMailer Exception: { $phpMailerExceptionMsg }");
}
@ -490,4 +554,18 @@ eoq;
*/
return $ret;
}
public function setSecureProtocol($smtpSsl): void
{
$this->protocol = ($smtpSsl) ? "ssl://" : "tcp://";
if ($smtpSsl == 1) {
$this->protocol = "ssl://";
$this->SMTPSecure = 'ssl';
}
if ($smtpSsl == 2) {
$this->protocol = "ssl://";
$this->SMTPSecure = 'tls';
}
}
} // end class definition

View file

@ -341,7 +341,7 @@ class SugarTheme
}
if (!inDeveloperMode()) {
if (is_file($cachedfile = sugar_cached($this->getFilePath().'/pathCache.php'))) {
$caches = unserialize(file_get_contents($cachedfile));
$caches = unserialize(file_get_contents($cachedfile), ['allowed_classes' => false]);
if (isset($caches['jsCache'])) {
$this->_jsCache = $caches['jsCache'];
}
@ -357,7 +357,7 @@ class SugarTheme
}
$cachedfile = sugar_cached($this->getFilePath().'/spriteCache.php');
if (!empty($GLOBALS['sugar_config']['use_sprites']) && is_file($cachedfile)) {
$this->_spriteCache = unserialize(sugar_file_get_contents($cachedfile));
$this->_spriteCache = unserialize(sugar_file_get_contents($cachedfile), ['allowed_classes' => false]);
}
}
$this->_initialCacheSize = array(

View file

@ -75,12 +75,25 @@ class Sugar_Smarty extends Smarty
mkdir_recursive(SUGAR_SMARTY_DIR . 'cache', true);
}
$this->template_dir = '.';
$this->compile_dir = SUGAR_SMARTY_DIR . 'templates_c';
$this->config_dir = SUGAR_SMARTY_DIR . 'configs';
$this->cache_dir = SUGAR_SMARTY_DIR . 'cache';
//$this->request_use_auto_globals = true; // to disable Smarty from using long arrays
$this->setTemplateDir('.');
$this->setCompileDir(SUGAR_SMARTY_DIR . 'templates_c');
$this->setCacheDir(SUGAR_SMARTY_DIR . 'cache');
$this->setConfigDir(SUGAR_SMARTY_DIR . 'configs');
$this->registerPHPFunctions([
'count',
'intval',
'is_string',
'key',
'preg_match',
'substr',
'strpos',
'strstr',
'strtoupper',
'strval',
'url2html'
]);
//TODO fix literals
$this->auto_literal = false;
@ -94,7 +107,16 @@ class Sugar_Smarty extends Smarty
$this->assign("VERSION_MARK", getVersionedPath(''));
}
public function registerPHPFunctions(array $functions): void
{
foreach ($functions as $function) {
try {
$this->registerPlugin(Smarty::PLUGIN_MODIFIER, $function, $function);
} catch (SmartyException $e) {
$GLOBALS['log']->fatal('Smarty: Failed to register PHP function' . $function . ': ' . $e->getMessage());
}
}
}
/**
* Override default _unlink method call to fix Bug 53010

View file

@ -125,10 +125,11 @@ class SugarWidgetSubPanelDetailViewLink extends SugarWidgetField
$detailView = $layout_def['DetailView'] ?? '';
$ownerId = $layout_def['owner_id'] ?? '';
$ownerModule = $layout_def['owner_module'] ?? '';
$groupAccessView = SecurityGroup::groupHasAccess($module,$record,'view');
if (!empty($record) &&
($detailView && !$layout_def['owner_module']
|| $detailView && !ACLController::moduleSupportsACL($layout_def['owner_module'])
|| ACLController::checkAccess($ownerModule, 'view', $ownerId == $current_user->id))) {
|| $detailView && !ACLController::moduleSupportsACL($layout_def['owner_module'])
|| ACLController::checkAccess($ownerModule, 'view', $ownerId == $current_user->id, 'module', $groupAccessView))) {
$link = ajaxLink("index.php?module=$module&action=$action&record={$record}{$parent}");
if ($module == 'EAPM') {
$link = "index.php?module=$module&action=$action&record={$record}{$parent}";

View file

@ -115,8 +115,7 @@ class SugarWidgetSubPanelRemoveButton extends SugarWidgetField
$hideremove = true;
}
//based on listview since that lets you select records
if ($layout_def['ListView'] && !$hideremove) {
if ($layout_def['EditView'] && !$hideremove) {
$retStr = "<a href=\"javascript:sub_p_rem('$subpanel', '$linked_field'"
. ", '$record', $refresh_page);\""
. ' class="listViewTdToolsS1"'

View file

@ -308,6 +308,12 @@ $app_list_strings = array(
'Dr.' => 'Dr.',
'Prof.' => 'Prof.',
),
'redirect_uri_type_dom' => [
'pretty_url' => 'Pretty URL (/ep/)',
'query_string' => 'Query String (index.php?entryPoint=)'
],
//time is in seconds; the greater the time the longer it takes;
'reminder_max_time' => 90000,
'reminder_time_options' => array(
@ -750,6 +756,12 @@ $app_list_strings = array(
'oauth' => 'OAuth',
],
'dom_outbound_email_auth_types' => [
'no_auth' => 'No Auth',
'basic' => 'Basic Auth',
'oauth' => 'OAuth',
],
'dom_external_oauth_connection_types' => [
'personal' => 'Personal',
'group' => 'Group',
@ -1406,6 +1418,7 @@ $app_strings = array(
'LBL_EMAIL_SETTINGS_CHECK_INTERVAL' => 'Check for New Mail',
'LBL_EMAIL_SETTINGS_FROM_ADDR' => 'From Address',
'LBL_EMAIL_SETTINGS_FROM_TO_EMAIL_ADDR' => 'Email Address For Test Notification:',
'LBL_EMAIL_SETTINGS_FROM_ADDR_NOT_SET' => 'From address and/or From name not set',
'LBL_EMAIL_SETTINGS_FROM_NAME' => 'From Name',
'LBL_EMAIL_SETTINGS_REPLY_TO_ADDR' => 'Reply to Address',
'LBL_EMAIL_SETTINGS_FULL_SYNC' => 'Synchronize All Mail Accounts',
@ -4020,6 +4033,8 @@ $app_strings['LBL_VALUE_SET_PLACEHOLDER'] = 'Value set. Enter new value to overr
$app_strings['ERR_IMAP_OAUTH_CONNECTION_ERROR'] = 'Not able to connect using OAuth login with Inbound Email server. For connection: ';
$app_strings['WARN_OAUTH_TOKEN_SESSION_EXPIRED'] = 'Your IMAP OAuth session has expired, please login again in the connection: ';
$app_strings['ERR_OAUTH_CONNECTION_ERROR'] = 'Not able to connect using OAuth login. For connection: ';
$app_strings['LBL_KEY'] = 'Key';
$app_strings['LBL_VALUE'] = 'Value';
$app_strings['LBL_OPTIONAL'] = 'Optional';

View file

@ -154,7 +154,7 @@ class nusoap_wsdlcache
$this->debug("$wsdl ($filename) not in cache (2)");
}
$this->releaseMutex($filename);
return (!is_null($s)) ? unserialize($s) : null;
return (!is_null($s)) ? unserialize($s, ['allowed_classes' => false]) : null;
}
$this->debug("Unable to obtain mutex for $filename in get");

View file

@ -2423,7 +2423,7 @@ function clean_xss($str, $cleanImg = true)
$sugar_config['email_xss'] = getDefaultXssTags();
}
$xsstags = unserialize(base64_decode($sugar_config['email_xss']));
$xsstags = unserialize(base64_decode($sugar_config['email_xss']), ['allowed_classes' => false]);
// cn: bug 13079 - "on\w" matched too many non-events (cONTact, strONG, etc.)
$jsEvents = 'onblur|onfocus|oncontextmenu|onresize|onscroll|onunload|ondblclick|onclick|';
@ -6005,7 +6005,7 @@ function sugar_unserialize($value)
return false;
}
return unserialize($value);
return unserialize($value, ['allowed_classes' => false]);
}
define('DEFAULT_UTIL_SUITE_ENCODING', 'UTF-8');

View file

@ -589,343 +589,6 @@ EOQ;
//--End of scenarios
//---------------
// SMTP Settings
//-------------------->
// smtp
// TODO-t: test it for all types
$MAIL_SSL_OPTIONS_GMAIL = get_select_options_with_id($app_list_strings['email_settings_for_ssl'], '2');
//$MAIL_SSL_OPTIONS_YAHOO = get_select_options_with_id($app_list_strings['email_settings_for_ssl'], '1');
$MAIL_SSL_OPTIONS_EXCHG = get_select_options_with_id($app_list_strings['email_settings_for_ssl'], 'none');
$MAIL_SSL_OPTIONS_OTHER = get_select_options_with_id($app_list_strings['email_settings_for_ssl'], 'none');
// set default notify_allow_default_outbound checkbox value
$notify_allow_default_outbound_checked = empty($_SESSION['notify_allow_default_outbound']) ? '' : ' checked="checked" ';
// set default smtp toggle buttons selected value
if (empty($_SESSION['smtp_tab_selected'])) {
$_SESSION['smtp_tab_selected'] = 'smtp_tab_other';
}
if (!isset($_SESSION['smtp_from_name']) || !$_SESSION['smtp_from_name']) {
$_SESSION['smtp_from_name'] = 'SuiteCRM';
}
if (!isset($_SESSION['smtp_from_addr']) || !$_SESSION['smtp_from_addr']) {
$_SESSION['smtp_from_addr'] = 'do_not_reply@example.com';
}
$out .= <<<EOQ
<div class="floatbox full" id="fb2">
<!-- smtp settings -->
<h3 onclick="$(this).next().toggle();" class="toggler">&raquo; {$mod_strings['LBL_MAIL_SMTP_SETTINGS']}</h3>
<div style="display: none;">
<br>
<!--
<p>{$mod_strings['LBL_WIZARD_SMTP_DESC']}</p>
-->
<!-- smtp types toggler buttons -->
<p style="display: inline;">
<div>
<div class="formrow">
<label>{$mod_strings['LBL_FROM_NAME']}</label>
<input type="text" name="smtp_from_name" value="{$_SESSION['smtp_from_name']}">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_FROM_ADDR']}</label>
<input type="email" name="smtp_from_addr" value="{$_SESSION['smtp_from_addr']}">
</div>
</div>
<div class="clear"></div>
{$mod_strings['LBL_CHOOSE_EMAIL_PROVIDER']} </p><div class="tooltip-toggle"> <em>i</em> <div class="tooltip">{$mod_strings['LBL_WIZARD_SMTP_DESC']}</div></div>
<div class="clear"></div>
<div>
<input type="button" class="smtp_tab_toggler" id="smtp_tab_gmail_toggler" for="smtp_tab_gmail" value="{$mod_strings['LBL_SMTPTYPE_GMAIL']}" />
<input type="button" class="smtp_tab_toggler" id="smtp_tab_yahoo_toggler" for="smtp_tab_yahoo" value="{$mod_strings['LBL_SMTPTYPE_YAHOO']}" />
<input type="button" class="smtp_tab_toggler" id="smtp_tab_exchange_toggler" for="smtp_tab_exchange" value="{$mod_strings['LBL_SMTPTYPE_EXCHANGE']}" />
<input type="button" class="smtp_tab_toggler selected" id="smtp_tab_other_toggler" for="smtp_tab_other" value="{$mod_strings['LBL_SMTPTYPE_OTHER']}" />
<input type="hidden" name="smtp_tab_selected" value="{$_SESSION['smtp_tab_selected']}">
</div>
<!-- smtp / gmail tab -->
<div class="form_section smtp_tab" id="smtp_tab_gmail">
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPSERVER']}</label>
<input type="text" name="smtp_tab_gmail[mail_smtpserver]" size="25" maxlength="255" value="smtp.gmail.com">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPPORT']}</label>
<input type="text" name="smtp_tab_gmail[mail_smtpport]" size="5" maxlength="5" value="587">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPAUTH_REQ']}</label>
<input type="checkbox" name="smtp_tab_gmail[mail_smtpauth_req]" id="smtp_tab_gmail__mail_smtpauth_req" value="1" checked="checked" onclick="toggleSMTPAuthSettings(this, 'toggleArea_1');">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_EMAIL_SMTP_SSL_OR_TLS']}</label>
<select name="smtp_tab_gmail[mail_smtpssl]">
{$MAIL_SSL_OPTIONS_GMAIL}
</select>
</div>
<div class="clear"></div>
<div class="toggleArea" id="toggleArea_1">
<div class="formrow">
<label>{$mod_strings['LBL_GMAIL_SMTPUSER']}</label>
<input type="text" name="smtp_tab_gmail[mail_smtpuser]" id="smtp_tab_gmail__mail_smtpuser" size="25" maxlength="255">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_GMAIL_SMTPPASS']}</label>
<input type="password" name="smtp_tab_gmail[mail_smtppass]" id="smtp_tab_gmail__mail_smtppass" size="25" maxlength="255" value="" tabindex="1">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION']} <i>i<div class="tooltip">{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION_HELP']}</div></i></label>
<input name="smtp_tab_gmail[notify_allow_default_outbound]" id="smtp_tab_gmail__notify_allow_default_outbound" value="2" tabindex="1" class="checkbox" type="checkbox" {$notify_allow_default_outbound_checked}>
</div>
</div>
<div class="clear"></div>
</div>
<!-- smtp / yahoo! mail tab -->
<div class="form_section smtp_tab" id="smtp_tab_yahoo">
<input type="hidden" name="smtp_tab_yahoo[mail_smtpserver]" size="25" maxlength="255" value="smtp.mail.yahoo.com">
<input type="text" name="smtp_tab_yahoo[mail_smtpport]" size="5" maxlength="5" value="465">
<input type="hidden" name="smtp_tab_yahoo[mail_smtpssl]" value="1">
<div class="formrow">
<label>{$mod_strings['LBL_YAHOOMAIL_SMTPUSER']}</label>
<input type="text" name="smtp_tab_yahoo[mail_smtpuser]" size="25" maxlength="255">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_YAHOOMAIL_SMTPPASS']}</label>
<input type="password" name="smtp_tab_yahoo[mail_smtppass]" size="25" maxlength="255" value="" tabindex="1">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION']} <i>i<div class="tooltip">{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION_HELP']}</div></i></label>
<input name="smtp_tab_yahoo[notify_allow_default_outbound]" value="2" tabindex="1" class="checkbox" type="checkbox" {$notify_allow_default_outbound_checked}>
</div>
<div class="clear"></div>
</div>
<!-- smtp / ms-exchange tab -->
<div class="form_section smtp_tab" id="smtp_tab_exchange">
<div class="formrow">
<label>{$mod_strings['LBL_EXCHANGE_SMTPSERVER']}</label>
<input type="text" name="smtp_tab_exchange[mail_smtpserver]" size="25" maxlength="255" value="">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_EXCHANGE_SMTPPORT']}</label>
<input type="text" name="smtp_tab_exchange[mail_smtpport]" size="5" maxlength="5" value="25">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPAUTH_REQ']}</label>
<input type="checkbox" name="smtp_tab_exchange[mail_smtpauth_req]" id="smtp_tab_exchange__mail_smtpauth_req" value="1" checked="checked" onclick="toggleSMTPAuthSettings(this, 'toggleArea_2');">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_EMAIL_SMTP_SSL_OR_TLS']}</label>
<select name="smtp_tab_exchange[mail_smtpssl]" tabindex="501">
{$MAIL_SSL_OPTIONS_EXCHG}
</select>
</div>
<div class="clear"></div>
<div class="toggleArea" id="toggleArea_2">
<div class="formrow">
<label>{$mod_strings['LBL_EXCHANGE_SMTPUSER']}</label>
<input type="text" name="smtp_tab_exchange[mail_smtpuser]" id="smtp_tab_exchange__mail_smtpuser" size="25" maxlength="255">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_EXCHANGE_SMTPPASS']}</label>
<input type="password" name="smtp_tab_exchange[mail_smtppass]" id="smtp_tab_exchange__mail_smtppass" size="25" maxlength="255" value="" tabindex="1">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION']} <i>i<div class="tooltip">{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION_HELP']}</div></i></label>
<input name="smtp_tab_exchange[notify_allow_default_outbound]" id="smtp_tab_exchange__notify_allow_default_outbound" value="2" tabindex="1" class="checkbox" type="checkbox" {$notify_allow_default_outbound_checked}>
</div>
</div>
<div class="clear"></div>
</div>
<!-- smtp / other tab-->
<div class="form_section smtp_tab" id="smtp_tab_other">
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPSERVER']}</label>
<input type="text" name="smtp_tab_other[mail_smtpserver]" size="25" maxlength="255" value="">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPPORT']}</label>
<input type="text" name="smtp_tab_other[mail_smtpport]" size="5" maxlength="5" value="25">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPAUTH_REQ']}</label>
<input type="hidden" name="smtp_tab_other[mail_smtpauth_req]" value="0">
<input type="checkbox" id="mail_smtpauth_req_chk" name="smtp_tab_other[mail_smtpauth_req]" value="1" checked="checked" onclick="toggleSMTPAuthSettings(this, 'toggleArea_3');">
</div>
<div class="formrow">
<label>{$mod_strings['LBL_EMAIL_SMTP_SSL_OR_TLS']}</label>
<select name="smtp_tab_other[mail_smtpssl]" tabindex="501">
{$MAIL_SSL_OPTIONS_OTHER}
</select>
</div>
<div class="clear"></div>
<div class="toggleArea" id="toggleArea_3">
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPUSER']}</label>
<input type="text" name="smtp_tab_other[mail_smtpuser]" id="smtp_tab_other__mail_smtpuser" size="25" maxlength="255">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_MAIL_SMTPPASS']}</label>
<input type="password" name="smtp_tab_other[mail_smtppass]" id="smtp_tab_other__mail_smtppass" size="25" maxlength="255" value="" tabindex="1">
</div>
<div class="clear"></div>
<div class="formrow">
<label>{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION']} <i>i<div class="tooltip">{$mod_strings['LBL_ALLOW_DEFAULT_SELECTION_HELP']}</div></i></label>
<input type="hidden" name="smtp_tab_other[notify_allow_default_outbound]" value="0">
<input id="notify_allow_default_outbound_chk" name="smtp_tab_other[notify_allow_default_outbound]" value="2" tabindex="1" class="checkbox" type="checkbox" {$notify_allow_default_outbound_checked}>
</div>
</div>
<div class="clear"></div>
<!-- </div> -->
<!-- smtp default values & tabs toggler js & tooltip help -->
<script>
var toggleSMTPAuthFields = {
toggleArea_1 : {
user: 'smtp_tab_gmail__mail_smtpuser',
pass: 'smtp_tab_gmail__mail_smtppass',
allow: 'smtp_tab_gmail__notify_allow_default_outbound'
},
toggleArea_2 : {
user: 'smtp_tab_exchange__mail_smtpuser',
pass: 'smtp_tab_exchange__mail_smtppass',
allow: 'smtp_tab_exchange__notify_allow_default_outbound'
},
toggleArea_3 : {
user: 'smtp_tab_other__mail_smtpuser',
pass: 'smtp_tab_other__mail_smtppass',
allow: 'notify_allow_default_outbound_chk'
}
};
var toggleSMTPAuthSettings = function(chkbox, elemID) {
if($(chkbox).prop('checked')) {
$('#' + elemID).show();
}
else {
$('#' + toggleSMTPAuthFields[elemID].user).val('');
$('#' + toggleSMTPAuthFields[elemID].pass).val('');
$('#' + toggleSMTPAuthFields[elemID].allow).prop('checked', false);
$('#' + elemID).hide();
}
};
$(function(){
$('.smtp_tab_toggler').click(function(){
$('.smtp_tab_toggler.selected').removeClass('selected');
$(this).addClass('selected');
$('.smtp_tab').hide();
$('#'+$(this).attr('for')).show();
$('input[name="smtp_tab_selected"]').val($(this).attr('for'));
});
// save last selected tab and set as default when form (re)load
$('#{$_SESSION['smtp_tab_selected']}_toggler').click();
$('select[name="smtp_tab_gmail[mail_smtpssl]"] option').each(function(){
if(!$(this).html()) {
$(this).html('-none-');
}
});
$('select[name="smtp_tab_yahoo[mail_smtpssl]"] option').each(function(){
if(!$(this).html()) {
$(this).html('-none-');
}
});
$('select[name="smtp_tab_exchange[mail_smtpssl]"] option').each(function(){
if(!$(this).html()) {
$(this).html('-none-');
}
});
$('select[name="smtp_tab_other[mail_smtpssl]"] option').each(function(){
if(!$(this).html()) {
$(this).html('-none-');
}
});
toggleSMTPAuthSettings(document.getElementById('smtp_tab_gmail__mail_smtpauth_req'), 'toggleArea_1');
toggleSMTPAuthSettings(document.getElementById('smtp_tab_exchange__mail_smtpauth_req'), 'toggleArea_2');
toggleSMTPAuthSettings(document.getElementById('mail_smtpauth_req_chk'), 'toggleArea_3');
});
</script>
</div> <!-- toggle hidden box end -->
EOQ;
// db setup (dbConfig_a.php)
$out2 =<<<EOQ2
<input type='hidden' name='setup_db_drop_tables' id='setup_db_drop_tables' value=''>
@ -956,8 +619,6 @@ EOQ2;
$out .= <<<EOQ
<!-- Branding -->
</div>
</div>
<div class="floatbox full" id="fb3">
<h3 onclick="$(this).next().toggle();" class="toggler">&raquo; {$mod_strings['LBL_WIZARD_SYSTEM_TITLE']}</h3>

View file

@ -1121,6 +1121,8 @@ EOQ;
RewriteBase {$basePath}
RewriteRule ^cache/jsLanguage/(.._..).js$ index.php?entryPoint=jslang&modulename=app_strings&lang=$1 [L,QSA]
RewriteRule ^cache/jsLanguage/(\w*)/(.._..).js$ index.php?entryPoint=jslang&modulename=$1&lang=$2 [L,QSA]
RewriteRule ^ep/(.*?)$ index.php?entryPoint=$1 [L,QSA]
# --------- DEPRECATED --------
RewriteRule ^api/(.*)$ - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

View file

@ -143,6 +143,7 @@ class SearchResults
$obj->load_relationships();
$fieldDefs = $obj->getFieldDefinitions();
$obj = $this->formatForDisplay($obj, $fieldDefs);
$parsed[$module][] = $this->updateFieldDefLinks($obj, $fieldDefs);
}
}
@ -150,6 +151,68 @@ class SearchResults
return $parsed;
}
/**
* Format data so it can be correctly displayed on search results
*
* @param SugarBean $obj
* @param array $fieldDefs
* @return SugarBean
*/
protected function formatForDisplay(SugarBean $obj, array $fieldDefs): SugarBean
{
global $app_list_strings, $locale;
foreach ($fieldDefs as $fieldDef) {
$value = $obj->{$fieldDef['name']};
if (isset($value)) {
switch ($fieldDef['type']){
case 'enum':
case 'dynamicenum':
if (isset($app_list_strings[$obj->field_name_map[$fieldDef['name']]['options']][$value]) &&
isset($obj->field_name_map[$fieldDef['name']]['options'])
) {
$obj->{$fieldDef['name']} = $app_list_strings[$obj->field_name_map[$fieldDef['name']]['options']][$value];
}
break;
case 'multienum':
if (isset($obj->field_name_map[$fieldDef['name']]['options']) &&
isset($app_list_strings[$obj->field_name_map[$fieldDef['name']]['options']])
) {
$multienumString = '';
$multienumValues = unencodeMultienum($value);
$arrayCount = count($multienumValues);
$i = 0;
foreach ($multienumValues as $multienumValue) {
$i++;
$multienumString .= $app_list_strings[$obj->field_name_map[$fieldDef['name']]['options']][$multienumValue];
if($i !== ($arrayCount)) $multienumString .= ", ";
}
$obj->{$fieldDef['name']} = $multienumString;
}
break;
case 'currency':
if (substr($fieldDef['name'], -9) !== '_usdollar') {
$params['currency_id'] = getCurrencyId($obj->module_dir, $obj->id);
$params['currency_symbol'] = $locale->currencies[$params['currency_id']]['symbol'];
} else {
$params['currency_id'] = $locale->getPrecedentPreference('currency');
$params['currency_symbol'] = $locale->getPrecedentPreference('default_currency_symbol');
$params['convert'] = true;
}
$obj->{$fieldDef['name']} = currency_format_number($value, $params);
break;
}
}
}
return $obj;
}
/**
*
* @param SugarBean $obj
@ -210,7 +273,12 @@ class SearchResults
{
$relField = $idName;
if (isset($obj->$link)) {
$relId = $obj->$link->getFocus()->$relField;
$linkedBeans = $obj->$link->getBeans();
if(count($linkedBeans) === 1){
$relId = array_keys($linkedBeans)[0];
} else {
$relId = $obj->$link->getFocus()->$relField;
}
if (is_object($relId)) {
if (method_exists($relId, "getFocus")) {
$relId = $relId->getFocus()->id;
@ -242,9 +310,9 @@ class SearchResults
*/
protected function getLink(string $label, string $module, string $record, string $action): string
{
global $sugar_config;
return "<a href=\"{$sugar_config['site_url']}/index.php?action={$action}&module={$module}&record={$record}&offset=1\"><span>{$label}</span></a>";
$link = ajaxLink("index.php?action={$action}&module={$module}&record={$record}");
return "<a href=\"{$link}\"><span>{$label}</span></a>";
}
/**

View file

@ -143,8 +143,15 @@ class SearchResultsController extends Controller
$smarty->assign('results', $this->results);
$smarty->assign('APP', $app_strings);
$smarty->assign('SITE_URL', $siteUrl);
$moduleName = [];
try {
$smarty->assign('resultsAsBean', $this->results->getHitsAsBeans());
$hitsAsBeans = $this->results->getHitsAsBeans();
foreach($hitsAsBeans as $bean){
$moduleName[$bean[0]->module_name] = translate('LBL_MODULE_NAME', $bean[0]->module_name);
}
$smarty->assign('moduleLabel', $moduleName);
$smarty->assign('resultsAsBean', $hitsAsBeans);
} catch (\SuiteCRM\Exception\Exception $e) {
LoggerManager::getLogger()->fatal("Failed to retrieve ElasticSearch options");
}

View file

@ -91,7 +91,7 @@
{/if}
{foreach from=$resultsAsBean item=beans key=module}
<h3>{$module}</h3>
<h3>{$moduleLabel[$module]}</h3>
<div class="unified-search-table-wrapper">
<table class="list view">
<thead>

View file

@ -406,7 +406,6 @@ EOF;
options: {
grouping:'$grouping',
backgroundGrid:false,
backgroundGrid:false,
gutterBottom: 150,
gutterTop:25,
gutterLeft:128,

View file

@ -56,10 +56,10 @@ function display_condition_lines($focus, $field, $value, $view)
while ($row = $focus->db->fetchByAssoc($result)) {
$condition_name = BeanFactory::newBean('AOR_Conditions');
$condition_name->retrieve($row['id']);
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path));
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path),['allowed_classes' => false]);
$html .= "report_fields = \"".trim(preg_replace('/\s+/', ' ', (string) getModuleFields(getRelatedModule($focus->report_module, $condition_name->module_path[0]))))."\";";
if ($condition_name->value_type == 'Date') {
$condition_name->value = unserialize(base64_decode($condition_name->value));
$condition_name->value = unserialize(base64_decode($condition_name->value),['allowed_classes' => false]);
}
$condition_item = json_encode($condition_name->toArray());
$html .= "loadConditionLine(".$condition_item.");";
@ -84,9 +84,9 @@ function display_condition_lines($focus, $field, $value, $view)
while ($row = $focus->db->fetchByAssoc($result)) {
$condition_name = BeanFactory::newBean('AOR_Conditions');
$condition_name->retrieve($row['id']);
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path));
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path),['allowed_classes' => false]);
if ($condition_name->value_type == 'Date') {
$condition_name->value = unserialize(base64_decode($condition_name->value));
$condition_name->value = unserialize(base64_decode($condition_name->value),['allowed_classes' => false]);
}
$condition_item = json_encode($condition_name->toArray());
$html .= "loadConditionLine(".$condition_item.");";

View file

@ -61,7 +61,7 @@ function display_field_lines($focus, $field, $value, $view)
while ($row = $focus->db->fetchByAssoc($result)) {
$field_name = BeanFactory::newBean('AOR_Fields');
$field_name->retrieve($row['id']);
$field_name->module_path = unserialize(base64_decode($field_name->module_path));
$field_name->module_path = unserialize(base64_decode($field_name->module_path),['allowed_classes' => false]);
$html .= "report_fields = \"".trim(preg_replace('/\s+/', ' ', (string) getModuleFields(getRelatedModule($focus->report_module, $field_name->module_path[0]))))."\";";
$field_item = json_encode($field_name->toArray());
$html .= "loadFieldLine(".$field_item.");";

View file

@ -205,7 +205,7 @@ class AOR_Report extends Basic
$field = BeanFactory::newBean('AOR_Fields');
$field->retrieve($row['id']);
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_bean = new $beanList[$this->report_module]();
@ -311,7 +311,7 @@ class AOR_Report extends Basic
$GLOBALS['log']->fatal('ambiguous group display for report ' . $this->id);
} else {
if ($rowsCount == 1) {
$rows[0]['module_path'] = unserialize(base64_decode($rows[0]['module_path']));
$rows[0]['module_path'] = unserialize(base64_decode($rows[0]['module_path']),['allowed_classes' => false]);
if (!$rows[0]['module_path'][0]) {
$module = new $beanList[$this->report_module]();
$rows[0]['field_id_name'] = $module->field_defs[$rows[0]['field']]['id_name'] ? $module->field_defs[$rows[0]['field']]['id_name'] : $module->field_defs[$rows[0]['field']]['name'];
@ -440,7 +440,7 @@ class AOR_Report extends Basic
$field_label = str_replace(' ', '_', (string) $field->label);
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_module = $module;
$table_alias = $field_module->table_name;
@ -670,7 +670,7 @@ class AOR_Report extends Basic
$field = BeanFactory::newBean('AOR_Fields');
$field->retrieve($row['id']);
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_bean = new $beanList[$this->report_module]();
@ -905,7 +905,7 @@ class AOR_Report extends Basic
// End
}
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_bean = new $beanList[$this->report_module]();
@ -1047,7 +1047,7 @@ class AOR_Report extends Basic
$field = BeanFactory::newBean('AOR_Fields');
$field->retrieve($row['id']);
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_bean = new $beanList[$this->report_module]();
$field_module = $this->report_module;
$field_alias = $field_bean->table_name;
@ -1240,7 +1240,7 @@ class AOR_Report extends Basic
$field->label = str_replace(' ', '_', (string) $field->label) . $i;
$path = unserialize(base64_decode($field->module_path));
$path = unserialize(base64_decode($field->module_path),['allowed_classes' => false]);
$field_module = $module;
$table_alias = $field_module->table_name;
@ -1263,7 +1263,6 @@ class AOR_Report extends Basic
$field_module = $new_field_module;
}
}
$data = $field_module->field_defs[$field->field] ?? [];
if (!empty($data)){
@ -1339,10 +1338,11 @@ class AOR_Report extends Basic
unset($query['id_select'][$table_alias]);
}
if ($field->group_by == 1) {
if ($field->field_function != null) {
$unique = $field->group_by == 1 ? 'DISTINCT ' : '';
$select_field = $field->field_function . '(' . $unique . $select_field . ')';
} elseif ($field->group_by == 1) {
$query['group_by'][] = $select_field;
} elseif ($field->field_function != null) {
$select_field = $field->field_function . '(' . $select_field . ')';
} else {
$query['second_group_by'][] = $select_field;
}
@ -1489,14 +1489,14 @@ class AOR_Report extends Basic
$condition = BeanFactory::newBean('AOR_Conditions');
$condition->retrieve($row['id']);
$path = unserialize(base64_decode($condition->module_path));
$path = unserialize(base64_decode($condition->module_path),['allowed_classes' => false]);
$condition_module = $module;
$table_alias = $condition_module->table_name;
$oldAlias = $table_alias;
if (!empty($path[0]) && $path[0] != $module->module_dir) {
foreach ($path as $rel) {
if (empty($rel)) {
if (empty($rel) || !is_string($rel)) {
continue;
}
// Bug: Prevents relationships from loading.
@ -1628,7 +1628,7 @@ class AOR_Report extends Basic
break;
case 'Date':
$params = unserialize(base64_decode($condition->value));
$params = unserialize(base64_decode($condition->value),['allowed_classes' => false]);
// Fix for issue #1272 - AOR_Report module cannot update Date type parameter.
if ($params == false) {

View file

@ -185,7 +185,7 @@ function getConditionsAsParameters($report, $override = array())
continue;
}
$path = unserialize(base64_decode($condition->module_path));
$path = unserialize(base64_decode($condition->module_path),['allowed_classes' => false]);
$field_module = $report->report_module;
if ($path[0] != $report->report_module) {
foreach ($path as $rel) {
@ -196,7 +196,7 @@ function getConditionsAsParameters($report, $override = array())
}
}
$additionalConditions = unserialize(base64_decode($condition->value));
$additionalConditions = unserialize(base64_decode($condition->value),['allowed_classes' => false]);
$value = isset($override[$condition->id]['value']) ? $override[$condition->id]['value'] : $value = $condition->value;

View file

@ -38,9 +38,9 @@ class AOR_ReportsViewDetail extends ViewDetail
if (!$condition->parameter) {
continue;
}
$condition->module_path = implode(":", unserialize(base64_decode($condition->module_path)));
$condition->module_path = implode(":", unserialize(base64_decode($condition->module_path),['allowed_classes' => false]));
if ($condition->value_type == 'Date') {
$condition->value = unserialize(base64_decode($condition->value));
$condition->value = unserialize(base64_decode($condition->value),['allowed_classes' => false]);
}
$condition_item = $condition->toArray();
$display = getDisplayForField($condition->module_path, $condition->field, $this->bean->report_module);

View file

@ -82,10 +82,10 @@ class AOR_ReportsViewEdit extends ViewEdit
$condition_name = BeanFactory::newBean('AOR_Conditions');
$condition_name->retrieve($row['id']);
if (!$condition_name->parenthesis) {
$condition_name->module_path = implode(":", unserialize(base64_decode($condition_name->module_path)));
$condition_name->module_path = implode(":", unserialize(base64_decode($condition_name->module_path),['allowed_classes' => false]));
}
if ($condition_name->value_type == 'Date') {
$condition_name->value = unserialize(base64_decode($condition_name->value));
$condition_name->value = unserialize(base64_decode($condition_name->value),['allowed_classes' => false]);
}
$condition_item = $condition_name->toArray();
@ -115,7 +115,7 @@ class AOR_ReportsViewEdit extends ViewEdit
while ($row = $this->bean->db->fetchByAssoc($result)) {
$field_name = BeanFactory::newBean('AOR_Fields');
$field_name->retrieve($row['id']);
$field_name->module_path = implode(":", unserialize(base64_decode($field_name->module_path)));
$field_name->module_path = implode(":", unserialize(base64_decode($field_name->module_path),['allowed_classes' => false]));
$arr = $field_name->toArray();

View file

@ -91,7 +91,7 @@ class AOR_Scheduled_Reports extends basic
public function get_email_recipients()
{
$params = unserialize(base64_decode($this->email_recipients));
$params = unserialize(base64_decode($this->email_recipients),['allowed_classes' => false]);
$emails = array();
if (isset($params['email_target_type'])) {

View file

@ -42,7 +42,7 @@ function display_email_lines($focus, $field, $value, $view)
{
global $app_list_strings;
$aorEmailToList = $app_list_strings['aor_email_to_list'] ?? '';
$params = unserialize(base64_decode($value));
$params = unserialize(base64_decode($value),['allowed_classes' => false]);
if ($view == 'EditView') {
$html = '<script src="modules/AOR_Scheduled_Reports/emailRecipients.js"></script>';

View file

@ -67,7 +67,7 @@ class templateParser
if (isset($field_def['name']) && $field_def['name'] != '') {
$fieldName = $field_def['name'];
if (empty($focus->$fieldName)) {
if (!isset($focus->$fieldName) || $focus->$fieldName === '') {
$repl_arr[$key . '_' . $fieldName] = '';
continue;
}

View file

@ -184,7 +184,7 @@ function display_lines($focus, $field, $value, $view)
if ($serviceCount == 0) {
$service .= "<tr>";
$service .= "<td width='5%' class='tabDetailViewDL' style='text-align: left;padding:2px;' scope='row'>&nbsp;</td>";
$service .= "<td width='46%' class='dataLabel' style='text-align: left;padding:2px;' colspan='2' scope='row'>".$mod_strings['LBL_SERVICE_NAME']."</td>";
$service .= "<td width='22%' class='dataLabel' style='text-align: left;padding:2px;' colspan='2' scope='row'>".$mod_strings['LBL_SERVICE_NAME']."</td>";
$service .= "<td width='12%' class='dataLabel' style='text-align: right;padding:2px;' scope='row'>".$mod_strings['LBL_SERVICE_LIST_PRICE']."</td>";
$service .= "<td width='12%' class='dataLabel' style='text-align: right;padding:2px;' scope='row'>".$mod_strings['LBL_SERVICE_DISCOUNT']."</td>";
$service .= "<td width='12%' class='dataLabel' style='text-align: right;padding:2px;' scope='row'>".$mod_strings['LBL_SERVICE_PRICE']."</td>";

View file

@ -57,13 +57,13 @@ function display_condition_lines($focus, $field, $value, $view)
while ($row = $focus->db->fetchByAssoc($result)) {
$condition_name = BeanFactory::newBean('AOW_Conditions');
$condition_name->retrieve($row['id']);
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path));
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path), ['allowed_classes' => false]);
if ($condition_name->module_path == '') {
$condition_name->module_path = $focus->flow_module;
}
$html .= "flow_fields = \"".trim(preg_replace('/\s+/', ' ', (string) getModuleFields(getRelatedModule($focus->flow_module, $condition_name->module_path[0]))))."\";";
if ($condition_name->value_type == 'Date') {
$condition_name->value = unserialize(base64_decode($condition_name->value));
$condition_name->value = unserialize(base64_decode($condition_name->value), ['allowed_classes' => false]);
}
$condition_item = json_encode($condition_name->toArray());
$html .= "loadConditionLine(".$condition_item.");";
@ -88,13 +88,13 @@ function display_condition_lines($focus, $field, $value, $view)
while ($row = $focus->db->fetchByAssoc($result)) {
$condition_name = BeanFactory::newBean('AOW_Conditions');
$condition_name->retrieve($row['id']);
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path));
$condition_name->module_path = unserialize(base64_decode($condition_name->module_path), ['allowed_classes' => false]);
if (empty($condition_name->module_path)) {
$condition_name->module_path[0] = $focus->flow_module;
}
$html .= "flow_fields = \"".trim(preg_replace('/\s+/', ' ', (string) getModuleFields(getRelatedModule($focus->flow_module, $condition_name->module_path[0]))))."\";";
if ($condition_name->value_type == 'Date') {
$condition_name->value = unserialize(base64_decode($condition_name->value));
$condition_name->value = unserialize(base64_decode($condition_name->value), ['allowed_classes' => false]);
}
$condition_item = json_encode($condition_name->toArray());
$html .= "loadConditionLine(".$condition_item.");";

View file

@ -389,7 +389,7 @@ class AOW_WorkFlow extends Basic
public function build_query_where(AOW_Condition $condition, $module, $query = array())
{
global $beanList, $app_list_strings, $sugar_config, $timedate;
$path = unserialize(base64_decode($condition->module_path));
$path = unserialize(base64_decode($condition->module_path), ['allowed_classes' => false]);
$condition_module = $module;
$table_alias = $condition_module->table_name;
@ -514,7 +514,7 @@ class AOW_WorkFlow extends Basic
return array();
case 'Date':
$params = @unserialize(base64_decode($condition->value));
$params = @unserialize(base64_decode($condition->value), ['allowed_classes' => false]);
if ($params === false) {
LoggerManager::getLogger()->error('Unserializable data given');
$params = [null];
@ -749,7 +749,7 @@ class AOW_WorkFlow extends Basic
$condition = BeanFactory::newBean('AOW_Conditions');
$condition->retrieve($row['id']);
$path = unserialize(base64_decode($condition->module_path));
$path = unserialize(base64_decode($condition->module_path), ['allowed_classes' => false]);
$condition_bean = $bean;
@ -815,7 +815,7 @@ class AOW_WorkFlow extends Basic
break;
case 'Date':
$params = unserialize(base64_decode($value));
$params = unserialize(base64_decode($value), ['allowed_classes' => false]);
$dateType = 'datetime';
if ($params[0] == 'now') {
$value = date('Y-m-d H:i:s');
@ -1055,7 +1055,7 @@ class AOW_WorkFlow extends Basic
$flow_action = new $action_name($action->id);
if (!$flow_action->run_action($bean, unserialize(base64_decode($action->parameters)), $in_save)) {
if (!$flow_action->run_action($bean, unserialize(base64_decode($action->parameters), ['allowed_classes' => false]), $in_save)) {
$pass = false;
$processed->aow_actions->add($action->id, array('status' => 'Failed'));
} else {

View file

@ -559,7 +559,7 @@ class AOW_WorkFlowController extends SugarController
$aow_action = BeanFactory::newBean('AOW_Actions');
$aow_action->retrieve($_REQUEST['id']);
$id = $aow_action->id;
$params = unserialize(base64_decode($aow_action->parameters ?? ''));
$params = unserialize(base64_decode($aow_action->parameters ?? ''), ['allowed_classes' => false]);
if ($params === false) {
$params = [];

View file

@ -206,7 +206,7 @@ if ($mail->Mailer == 'smtp' && $mail->Host == '') {
$focus = BeanFactory::newBean('InboundEmail');
$focus->checkImap();
$storedOptions = unserialize(base64_decode($focus->stored_options ?? ''));
$storedOptions = unserialize(base64_decode($focus->stored_options ?? ''), ['allowed_classes' => false]);
$email_templates_arr = get_bean_select_array(true, 'EmailTemplate', 'name', '', 'name', true);
$create_case_email_template = (isset($storedOptions['create_case_email_template'])) ? $storedOptions['create_case_email_template'] : "";
$TMPL_DRPDWN_LOST = get_select_options_with_id($email_templates_arr, $res['lostpasswordtmpl']);

View file

@ -66,7 +66,8 @@ class Controller extends AbstractController
*/
public function doSave(): void
{
$searchEngine = filter_input(INPUT_POST, 'search-engine');
$searchEngine = filter_input(INPUT_POST, 'search-engine', FILTER_SANITIZE_STRING);
$aod = $searchEngine === 'BasicAndAodEngine';
SearchConfigurator::make()
->setEngine($searchEngine)

View file

@ -98,7 +98,7 @@ $has_updates= false;
if (!empty($license->settings['license_latest_versions'])) {
$encodedVersions = $license->settings['license_latest_versions'];
$versions = unserialize(base64_decode($encodedVersions));
$versions = unserialize(base64_decode($encodedVersions), ['allowed_classes' => false]);
include('sugar_version.php');
if (!empty($versions)) {
foreach ($versions as $version) {

View file

@ -78,7 +78,7 @@ while ($row = $db->fetchByAssoc($result)) {
$prefs = array();
$newprefs = array();
$prefs = unserialize(base64_decode($row['user_preferences']));
$prefs = unserialize(base64_decode($row['user_preferences']), ['allowed_classes' => false]);
$setTo = '';
$alreadySet = '';
if (!empty($prefs)) {

View file

@ -196,7 +196,7 @@ function check_now($send_usage_info=true, $get_request_data=false, $response_dat
if ($response_data || !$sclient->getError()) {
$serializedResultData = sugarDecode($key, $encodedResult);
$resultData = unserialize($serializedResultData);
$resultData = unserialize($serializedResultData, ['allowed_classes' => false]);
if ($response_data && empty($resultData)) {
$resultData = array();
$resultData['validation'] = 'invalid validation key';

View file

@ -231,7 +231,7 @@ class CalendarDisplay
if (empty($activity)) {
$activity = $this->activity_colors;
}
$newActivities = unserialize(base64_decode($current_user->getPreference("CalendarActivities") ?? ''));
$newActivities = unserialize(base64_decode($current_user->getPreference("CalendarActivities") ?? ''), ['allowed_classes' => false]);
if ($newActivities) {
$activity = array_merge($activity, $newActivities);
}

View file

@ -135,7 +135,7 @@ function get_campaign_mailboxes_with_stored_options()
$r = $db->query($q);
while ($a = $db->fetchByAssoc($r)) {
$ret[$a['id']] = unserialize(base64_decode($a['stored_options']));
$ret[$a['id']] = unserialize(base64_decode($a['stored_options']), ['allowed_classes' => false]);
}
return $ret;
}

View file

@ -49,21 +49,28 @@ class TemplateDatetimecombo extends TemplateRange
{
public $type = 'datetimecombo';
public $len = '';
public $dateStrings = array(
'-none-' => '',
'today'=>'now',
'yesterday'=> '-1 day',
'tomorrow'=>'+1 day',
'next week'=> '+1 week',
'next monday'=>'next monday',
'next friday'=>'next friday',
'two weeks'=> '+2 weeks',
'next month'=> '+1 month',
'first day of next month'=> 'first of next month', // must handle this non-GNU date string in SugarBean->populateDefaultValues; if we don't this will evaluate to 1969...
'three months'=> '+3 months', //kbrill Bug #17023
'six months'=> '+6 months',
'next year'=> '+1 year',
);
public $dateStrings;
public function __construct()
{
parent::__construct();
global $app_strings;
$this->dateStrings = array(
$app_strings['LBL_NONE']=>'',
$app_strings['LBL_YESTERDAY']=> '-1 day',
$app_strings['LBL_TODAY']=>'now',
$app_strings['LBL_TOMORROW']=>'+1 day',
$app_strings['LBL_NEXT_WEEK']=> '+1 week',
$app_strings['LBL_NEXT_MONDAY']=>'next monday',
$app_strings['LBL_NEXT_FRIDAY']=>'next friday',
$app_strings['LBL_TWO_WEEKS']=> '+2 weeks',
$app_strings['LBL_NEXT_MONTH']=> '+1 month',
$app_strings['LBL_FIRST_DAY_OF_NEXT_MONTH']=> 'first day of next month', // must handle this non-GNU date string in SugarBean->populateDefaultValues; if we don't this will evaluate to 1969...
$app_strings['LBL_THREE_MONTHS']=> '+3 months', //kbrill Bug #17023
$app_strings['LBL_SIXMONTHS']=> '+6 months',
$app_strings['LBL_NEXT_YEAR']=> '+1 year',
);
}
public $hoursStrings = array(
'' => '',

View file

@ -153,7 +153,7 @@ class TemplateEnum extends TemplateText
$def['studio'] = 'visible';
// this class may be extended, so only do the unserialize for genuine TemplateEnums
if (get_class($this) == 'TemplateEnum' && empty($def['dependency'])) {
$def['dependency'] = $this->ext4 !== null? unserialize(html_entity_decode((string) $this->ext4)) : null ;
$def['dependency'] = $this->ext4 !== null? unserialize(html_entity_decode((string) $this->ext4), ['allowed_classes' => false]) : null ;
}
if (!empty($this->visibility_grid)) {
$def['visibility_grid'] = $this->visibility_grid;

View file

@ -128,7 +128,7 @@ class TemplateMultiEnum extends TemplateEnum
// turn off error reporting in case we are unpacking a value that hasn't been packed...
// this is kludgy, but unserialize doesn't throw exceptions correctly
if ($this->ext4[0] == 'a' && $this->ext4[1] == ':') {
$unpacked = @unserialize($this->ext4) ;
$unpacked = @unserialize($this->ext4, ['allowed_classes' => false]) ;
} else {
$unpacked = false;
}

View file

@ -50,7 +50,7 @@ require_once('modules/Notes/Note.php');
$note = BeanFactory::newBean('Notes');
//check if file is an email image
if (!$note->retrieve_by_string_fields(array('id' => $_REQUEST['id'], 'parent_type' => "Emails"))) {
//die("Not a Valid Entry Point");
die("Not a Valid Entry Point");
}
$location = $GLOBALS['sugar_config']['upload_dir']."/" . $_REQUEST['id'];

View file

@ -81,8 +81,7 @@ if (isset($admin->settings['massemailer_email_copy'])) {
$emailsPerSecond = 10;
$mail->setMailerForSystem();
$mail->From = "no-reply@example.com";
$mail->FromName = "no-reply";
$mail->setSystemFromAddress();
$mail->ContentType = "text/html";
$campaign_id = null;
@ -226,32 +225,29 @@ do {
$mail->Mailer = 'smtp';
$mail->Host = $outboundEmailAccount->mail_smtpserver;
$mail->Port = $outboundEmailAccount->mail_smtpport;
if ($outboundEmailAccount->mail_smtpssl == 1) {
$mail->SMTPSecure = 'ssl';
} elseif ($outboundEmailAccount->mail_smtpssl == 2) {
$mail->SMTPSecure = 'tls';
} else {
$mail->SMTPSecure = '';
}
if ($outboundEmailAccount->mail_smtpauth_req) {
$mail->SMTPAuth = true;
$mail->Username = $outboundEmailAccount->mail_smtpuser;
$mail->Password = $outboundEmailAccount->mail_smtppass;
} else {
$mail->SMTPAuth = false;
$mail->Username = '';
$mail->Password = '';
}
$mail->setSecureProtocol($ssltls ?? false);
$mail->initSMTPAuth(
$outboundEmailAccount->auth_type ?? '',
$outboundEmailAccount->external_oauth_connection_id ?? '',
$outboundEmailAccount->mail_smtpuser ?? '',
$outboundEmailAccount->mail_smtppass ?? '',
);
} else {
$mail->Mailer = 'sendmail';
}
$mail->oe->mail_smtpauth_req = $outboundEmailAccount->mail_smtpauth_req;
$mail->oe->mail_smtpuser = $outboundEmailAccount->mail_smtpuser;
$mail->oe->mail_smtppass = $outboundEmailAccount->mail_smtppass;
$mail->oe->mail_smtpserver = $outboundEmailAccount->mail_smtpserver;
$mail->oe->mail_smtpport = $outboundEmailAccount->mail_smtpport;
$mail->oe->mail_smtpssl = $outboundEmailAccount->mail_smtpssl;
$mail->oe->auth_type = $outboundEmailAccount->auth_type ?? '';
$mail->oe->external_oauth_connection_id = $outboundEmailAccount->external_oauth_connection_id ?? '';
$mail->oe->mail_smtpauth_req = $outboundEmailAccount->mail_smtpauth_req ?? '';
$mail->oe->mail_smtpuser = $outboundEmailAccount->mail_smtpuser ?? '';
$mail->oe->mail_smtppass = $outboundEmailAccount->mail_smtppass ?? '';
$mail->oe->mail_smtpserver = $outboundEmailAccount->mail_smtpserver ?? '';
$mail->oe->mail_smtpport = $outboundEmailAccount->mail_smtpport ?? '';
$mail->oe->mail_smtpssl = $outboundEmailAccount->mail_smtpssl ?? '';
$mail->FromName = $outboundEmailAccount->smtp_from_name ?? $outboundEmailAccount->mail_smtpuser;
$mail->From = $outboundEmailAccount->smtp_from_address ?? $outboundEmailAccount->mail_smtpuser;
}
if ((empty($row['related_confirm_opt_in']) || $row['related_confirm_opt_in'] == '0')) {

View file

@ -61,15 +61,6 @@ class EmailManController extends SugarController
$_REQUEST['mail_sendtype'] = "SMTP";
}
// save Outbound settings #Bug 20033 Ensure data for Outbound email exists before trying to update the system mailer.
if (isset($_REQUEST['mail_sendtype']) && empty($_REQUEST['campaignConfig'])) {
$oe = new OutboundEmail();
$oe->populateFromPost();
$oe->saveSystem();
}
$focus = BeanFactory::newBean('Administration');
if (isset($_POST['tracking_entities_location_type'])) {

View file

@ -139,6 +139,7 @@ $mod_strings = array(
'LBL_FROM_ADDRESS_HELP' => 'When enabled, the assigning user\\\'s name and email address will be included in the From field of the email. This feature might not work with SMTP servers that do not allow sending from a different email account than the server account.',
'LBL_HELP' => 'Help' /*for 508 compliance fix*/,
'LBL_OUTBOUND_EMAIL_ACCOUNT_VIEW' => 'View Outbound Email Accounts',
'LBL_SYSTEM_OUTBOUND_EMAIL_ACCOUNT' => 'System Outbound Email Account',
'LBL_ALLOW_SEND_AS_USER' => 'Users may send as themselves:',
'LBL_ALLOW_SEND_AS_USER_DESC' => 'When enabled, <b>all</b> users can send email using the outgoing mail server, using their own primary email address as the &quot;From:&quot; address.<br>This feature might not work with SMTP servers that do not allow sending from a different email account than the server account.',
);

View file

@ -52,9 +52,22 @@ global $mod_strings;
global $app_list_strings;
global $app_strings;
global $current_user;
global $sugar_config;
$testMaxTimeout = 30; // seconds
if (isset($sugar_config['outbound_email_test_max_timeout']) && is_numeric($sugar_config['outbound_email_test_max_timeout'])) {
$testMaxTimeout = (int)$sugar_config['outbound_email_test_max_timeout'];
}
ini_set('max_execution_time', $testMaxTimeout);
$json = getJSONobj();
$pass = '';
if (empty($_REQUEST['mail_smtppass'])) {
$_REQUEST['mail_smtppass'] = BeanFactory::getBean('OutboundEmailAccounts', $_REQUEST['record'])?->mail_smtppass ?? '';
}
if (!empty($_REQUEST['mail_smtppass'])) {
$pass = $_REQUEST['mail_smtppass'];
} elseif (isset($_REQUEST['mail_type'])) {
@ -73,13 +86,15 @@ $out = $email->sendEmailTest(
$_REQUEST['mail_smtpserver'],
$_REQUEST['mail_smtpport'],
$_REQUEST['mail_smtpssl'],
($_REQUEST['mail_smtpauth_req'] == 'true' ? 1 : 0),
(isTrue($_REQUEST['mail_smtpauth_req']) ? 1 : 0),
$_REQUEST['mail_smtpuser'],
$pass,
$_REQUEST['outboundtest_from_address'],
$_REQUEST['outboundtest_to_address'],
$_REQUEST['mail_sendtype'],
$_REQUEST['mail_from_name']
$_REQUEST['mail_from_name'],
$_REQUEST['mail_auth_type'] ?? 'no_auth',
$_REQUEST['mail_external_oauth_connection_id'] ?? '',
);
$out = $json->encode($out);

View file

@ -105,96 +105,39 @@ function change_state(radiobutton) {
{$MOD.LBL_OUTGOING_SECTION_HELP}
</td>
</tr>
<tr class="{$OUTBOUND_TYPE_CLASS}">
<td width="20%" scope="row">{$MOD.LBL_MAIL_SENDTYPE}</td>
<td width="30%">
<select id="mail_sendtype" name="mail_sendtype" onChange="notify_setrequired(document.ConfigureSettings); SUGAR.user.showHideGmailDefaultLink(this);" tabindex="1">{$mail_sendtype_options}</select>
</td>
<td scope="row" class="mobile-hide">&nbsp;</td>
<td class="mobile-hide">&nbsp;</td>
</tr>
<tr>
<td width="25%" scope="row">
{$MOD.LBL_SYSTEM_OUTBOUND_EMAIL_ACCOUNT}
</td>
{if empty($system_outbound_email_id)}
<td width="30%">
{sugar_link
module="OutboundEmailAccounts"
label=$APP.LBL_CREATE_BUTTON_LABEL
action='Edit'
class="btn btn-sm btn-primary"
extraparams="type=system"
}
</td>
{/if}
{if !empty($system_outbound_email_id)}
<td width="30%">
{sugar_link
module="OutboundEmailAccounts"
record=$system_outbound_email_id
label=$system_outbound_email_name
action='DetailView'
}
</td>
{/if}
<td></td>
</tr>
<tr>
<td width="20%" scope="row">{$MOD.LBL_NOTIFY_FROMNAME} <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%" > <input id='notify_fromname' name='notify_fromname' tabindex='1' size='25' maxlength='128' type="text" value="{$notify_fromname}"></td>
</tr>
<tr>
<td width="20%" scope="row">{$MOD.LBL_NOTIFY_FROMADDRESS} <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%"><input id='notify_fromaddress' name='notify_fromaddress' tabindex='1' size='25' maxlength='128' type="text" value="{$notify_fromaddress}"></td>
</tr>
<tr>
<td align="left" scope="row" colspan="4">{$MOD.LBL_CHOOSE_EMAIL_PROVIDER}</td>
</tr>
<tr>
<td colspan="4">
<div id="smtpButtonGroup" class="yui-buttongroup">
<span id="gmail" class="yui-button yui-radio-button{if $mail_smtptype == 'gmail'} yui-button-checked{/if}">
<span class="first-child">
<button type="button" name="mail_smtptype" value="gmail" class="btn btn-danger">
&nbsp;&nbsp;&nbsp;&nbsp;{$APP.LBL_SMTPTYPE_GMAIL}&nbsp;&nbsp;&nbsp;&nbsp;
</button>
</span>
</span>
<span id="yahoomail" class="yui-button yui-radio-button{if $mail_smtptype == 'yahoomail'} yui-button-checked{/if}">
<span class="first-child">
<button type="button" name="mail_smtptype" value="yahoomail" class="btn btn-danger">
&nbsp;&nbsp;&nbsp;&nbsp;{$APP.LBL_SMTPTYPE_YAHOO}&nbsp;&nbsp;&nbsp;&nbsp;
</button>
</span>
</span>
<span id="exchange" class="yui-button yui-radio-button{if $mail_smtptype == 'exchange'} yui-button-checked{/if}">
<span class="first-child">
<button type="button" name="mail_smtptype" value="exchange" class="btn btn-danger">
&nbsp;&nbsp;&nbsp;&nbsp;{$APP.LBL_SMTPTYPE_EXCHANGE}&nbsp;&nbsp;&nbsp;&nbsp;
</button>
</span>
</span>
<span id="other" class="yui-button yui-radio-button{if $mail_smtptype == 'other' || empty($mail_smtptype)} yui-button-checked{/if}">
<span class="first-child">
<button type="button" name="mail_smtptype" value="other" class="btn btn-danger">
&nbsp;&nbsp;&nbsp;&nbsp;{$APP.LBL_SMTPTYPE_OTHER}&nbsp;&nbsp;&nbsp;&nbsp;
</button>
</span>
</span>
</div>
</td>
</tr>
<tr>
<td colspan="4">
<td colspan="2">
<div id="smtp_settings">
<table width="100%" cellpadding="0" cellspacing="0">
<tr id="mailsettings1">
<td width="20%" scope="row"><span id="mail_smtpserver_label">{$MOD.LBL_MAIL_SMTPSERVER}</span> <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%" ><input type="text" id="mail_smtpserver" name="mail_smtpserver" tabindex="1" size="25" maxlength="255" value="{$mail_smtpserver}"></td>
<td width="20%" scope="row"><span id="mail_smtpport_label">{$MOD.LBL_MAIL_SMTPPORT}</span> <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%" ><input type="text" id="mail_smtpport" name="mail_smtpport" tabindex="1" size="5" maxlength="5" value="{$mail_smtpport}"></td>
</tr>
<tr id="mailsettings2">
<td scope="row"><span id='mail_smtpauth_req_label'>{$MOD.LBL_MAIL_SMTPAUTH_REQ}</span></td>
<td >
<input id='mail_smtpauth_req' name='mail_smtpauth_req' type="checkbox" class="checkbox" value="1" tabindex='1'
onclick="notify_setrequired(document.ConfigureSettings);" {$mail_smtpauth_req}>
</td>
<td width="15%" scope="row"><span id="mail_smtpssl_label">{$APP.LBL_EMAIL_SMTP_SSL_OR_TLS}</span></td>
<td width="35%" >
<select id="mail_smtpssl" name="mail_smtpssl" tabindex="501" onchange="setDefaultSMTPPort();" >{$MAIL_SSL_OPTIONS}</select>
</td>
</tr>
<tr id="smtp_auth1">
<td width="20%" scope="row"><span id="mail_smtpuser_label">{$MOD.LBL_MAIL_SMTPUSER}</span> <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%" ><input type="text" id="mail_smtpuser" name="mail_smtpuser" size="25" maxlength="255" value="{$mail_smtpuser}" tabindex='1' ></td>
<td width="20%" class="mobile-hide">&nbsp;</td>
<td width="30%" class="mobile-hide">&nbsp;</td>
</tr>
<tr id="smtp_auth2">
<td width="20%" scope="row"><span id="mail_smtppass_label">{$MOD.LBL_MAIL_SMTPPASS}</span> <span class="required">{$APP.LBL_REQUIRED_SYMBOL}</span></td>
<td width="30%" >
<input type="password" id="mail_smtppass" name="mail_smtppass" size="25" maxlength="255" tabindex='1'>
<a href="javascript:void(0)" id='mail_smtppass_link' onClick="SUGAR.util.setEmailPasswordEdit('mail_smtppass')" style="display: none">{$APP.LBL_CHANGE_PASSWORD}</a>
</td>
<td width="20%" class="mobile-hide">&nbsp;</td>
<td width="30%" class="mobile-hide">&nbsp;</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0">
<tr id="mail_allow_user">
<td width="25%" scope="row">
@ -203,7 +146,7 @@ function change_state(radiobutton) {
</td>
<td width="30%">
<input type='hidden' id="notify_allow_default_outbound_hidden_input" name='notify_allow_default_outbound' value='0'>
<input id="notify_allow_default_outbound" name='notify_allow_default_outbound' value="2" tabindex='1' class="checkbox" type="checkbox" {$notify_allow_default_outbound_on}>
<input id="notify_allow_default_outbound" name='notify_allow_default_outbound' value="2" tabindex='1' class="checkbox" type="checkbox" style="margin-top: 10px;" {$notify_allow_default_outbound_on}>
</td>
</tr>
<tr class="legacy-compose-option" {if isset($legacyEmailConfigEnabled)}style="display:none"{/if}>
@ -222,13 +165,6 @@ function change_state(radiobutton) {
</div>
</td>
</tr>
<tr><td colspan="4" class="mobile-hide">&nbsp;</tr>
<tr>
<td width="15%"><input type="button" class="btn btn-info" value="{$APP.LBL_EMAIL_TEST_OUTBOUND_SETTINGS}" onclick="testOutboundSettings();">&nbsp;</td>
<td width="15%" class="mobile-hide">&nbsp;</td>
<td width="40%" class="mobile-hide">&nbsp;</td>
<td width="40%" class="mobile-hide">&nbsp;</td>
</tr>
</table>
</div>
</div>

View file

@ -182,8 +182,12 @@ class ViewConfig extends SugarView
LoggerManager::getLogger()->warn('EmailMan view display error: mail allow user send is not set for focus');
}
$oe = new OutboundEmail();
$oe = $oe->getSystemEmail();
$this->ss->assign("system_outbound_email_id", $oe->id);
$this->ss->assign("system_outbound_email_name", $oe->name);
$this->ss->assign("mail_smtptype", $mailSmtpType);
$this->ss->assign("mail_smtpserver", $mailSmtpServer);
$this->ss->assign("mail_smtpport", $mailSmtpPort);
$this->ss->assign("mail_smtpuser", $mailSmtpUser);
@ -277,7 +281,7 @@ class ViewConfig extends SugarView
$sugar_config['email_xss'] = getDefaultXssTags();
}
foreach (unserialize(base64_decode($sugar_config['email_xss'])) as $k => $v) {
foreach (unserialize(base64_decode($sugar_config['email_xss']), ['allowed_classes' => false]) as $k => $v) {
$this->ss->assign($k."Checked", 'CHECKED');
}

View file

@ -60,7 +60,7 @@ if (!$focus->ACLAccess('Delete')) {
$focus->mark_deleted($_REQUEST['record']);
if (isset($_REQUEST['record'])) {
$query = "DELETE FROM emailman WHERE marketing_id ='" . $_REQUEST['record'] ."'";
$query = "DELETE FROM emailman WHERE marketing_id ='" . $focus->db->quote($_REQUEST['record']) ."'";
$focus->db->query($query);
}

View file

@ -287,7 +287,7 @@ class EmailTemplate extends SugarBean
public function fill_in_additional_detail_fields()
{
if (empty($this->body) && !empty($this->body_html)) {
if (!empty($this->body_html)) {
global $sugar_config;
$bodyCleanup = $this->body_html;

View file

@ -812,7 +812,9 @@ class Email extends Basic
$fromaddress,
$toaddress,
$mail_sendtype = 'smtp',
$fromname = ''
$fromname = '',
$authType = 'no_auth',
$externalOAuthConnectionId = ''
) {
global $current_user, $app_strings;
$mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); //Called from EmailMan as well.
@ -821,22 +823,14 @@ class Email extends Basic
if ($mail->Mailer == 'smtp') {
$mail->Host = $mailserver_url;
$mail->Port = $port;
if (isset($ssltls) && !empty($ssltls)) {
$mail->protocol = "ssl://";
if ($ssltls == 1) {
$mail->SMTPSecure = 'ssl';
} // if
if ($ssltls == 2) {
$mail->SMTPSecure = 'tls';
} // if
} else {
$mail->protocol = "tcp://";
}
if ($smtp_auth_req) {
$mail->SMTPAuth = true;
$mail->Username = $smtp_username;
$mail->Password = $smtppassword;
}
$mail->setSecureProtocol($ssltls ?? false);
$mail->initSMTPAuth(
$authType ?? '',
$externalOAuthConnectionId ?? '',
$smtp_username ?? '',
$smtppassword ?? ''
);
} else {
$mail->Mailer = 'sendmail';
}
@ -2789,18 +2783,14 @@ class Email extends Basic
$mail->Mailer = "smtp";
$mail->Host = $oe->mail_smtpserver;
$mail->Port = $oe->mail_smtpport;
if ($oe->mail_smtpssl == 1) {
$mail->SMTPSecure = 'ssl';
} // if
if ($oe->mail_smtpssl == 2) {
$mail->SMTPSecure = 'tls';
} // if
if ($oe->mail_smtpauth_req) {
$mail->SMTPAuth = true;
$mail->Username = $oe->mail_smtpuser;
$mail->Password = $oe->mail_smtppass;
}
$mail->setSecureProtocol($oe->mail_smtpssl);
$mail->initSMTPAuth(
$oe->auth_type ?? '',
$oe->external_oauth_connection_id ?? '',
$oe->mail_smtpuser ?? '',
$oe->mail_smtppass ?? '',
);
} else {
$mail->Mailer = "sendmail";
}
@ -2842,18 +2832,14 @@ class Email extends Basic
$mail->Mailer = "smtp";
$mail->Host = $oe->mail_smtpserver;
$mail->Port = $oe->mail_smtpport;
if ($oe->mail_smtpssl == 1) {
$mail->SMTPSecure = 'ssl';
} // if
if ($oe->mail_smtpssl == 2) {
$mail->SMTPSecure = 'tls';
} // if
if ($oe->mail_smtpauth_req) {
$mail->SMTPAuth = true;
$mail->Username = $oe->mail_smtpuser;
$mail->Password = $oe->mail_smtppass;
}
$mail->setSecureProtocol($oe->mail_smtpssl);
$mail->initSMTPAuth(
$oe->auth_type ?? '',
$oe->external_oauth_connection_id ?? '',
$oe->mail_smtpuser ?? '',
$oe->mail_smtppass ?? '',
);
} else {
$mail->Mailer = "sendmail";
}
@ -3295,15 +3281,12 @@ class Email extends Basic
*/
public function getSystemDefaultEmail()
{
$email = array();
$r1 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromaddress\'');
$r2 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromname\'');
$a1 = $this->db->fetchByAssoc($r1);
$a2 = $this->db->fetchByAssoc($r2);
$oe = new OutboundEmail();
$oe = $oe->getSystemMailerSettings();
$email['email'] = $a1['value'];
$email['name'] = $a2['value'];
$email['email'] = $oe->smtp_from_addr ?? '';
$email['name'] = $oe->smtp_from_name ?? '';
return $email;
}
@ -3419,7 +3402,7 @@ class Email extends Basic
);
}
}
if (empty($this->contact_id) && !empty($this->parent_id) && !empty($this->parent_type) && $this->parent_type === 'Contacts' && !empty($this->parent_name)) {
$this->contact_id = $this->parent_id;
$this->contact_name = $this->parent_name;
@ -4438,14 +4421,16 @@ eoq;
// is from address in the request?
if (!isset($request['from_addr_name']) || !$request['from_addr_name']) {
$useDefaultFromAddressName = true;
}
if (!isset($request['from_addr'])) {
if (!isset($request['from_addr_name']) || !$request['from_addr_name']) {
$useDefaultFromAddressName = true;
}
// is from name in the request?
// is from name in the request?
if (!isset($request['from_addr_email']) || !$request['from_addr_email']) {
$useDefaultFromAddressEmail = true;
if (!isset($request['from_addr_email']) || !$request['from_addr_email']) {
$useDefaultFromAddressEmail = true;
}
}
// so, do we have to use any default data?

View file

@ -1561,7 +1561,7 @@ HTML;
}
if (file_exists($cache)) {
include($cache); // profides $cacheFile
$metaOut = unserialize($cacheFile['out']);
$metaOut = unserialize($cacheFile['out'], ['allowed_classes' => false]);
$meta = $metaOut['meta']['email'];
if (isset($meta['attachments'])) {
$attachmentHtmlData = $meta['attachments'];
@ -3391,9 +3391,7 @@ eoq;
include($cacheFilePath); // provides $cacheFile
if (isset($cacheFile[$key])) {
$ret = unserialize($cacheFile[$key]);
return $ret;
return unserialize($cacheFile[$key], ['allowed_classes' => false]);
}
} else {
$GLOBALS['log']->debug("EMAILUI: cache file not found [ {$cacheFilePath} ] - creating blank cache file");

View file

@ -1510,7 +1510,7 @@ eoq;
foreach ($ie->field_defs as $k => $v) {
if ($k == 'stored_options') {
$ie->$k = unserialize(base64_decode($ie->$k));
$ie->$k = unserialize(base64_decode($ie->$k), ['allowed_classes' => false]);
if (isset($ie->stored_options['from_name'])) {
$ie->stored_options['from_name'] = from_html($ie->stored_options['from_name']);
}

View file

@ -498,7 +498,7 @@ class ListViewDataEmails extends ListViewData
$ret = $emailHeader['flagged'];
break;
case 'name':
$ret = html_entity_decode((string) $inboundEmail->handleMimeHeaderDecode($emailHeader['subject'] ?? ''));
$ret = html_entity_decode(purify_html((string) $inboundEmail->handleMimeHeaderDecode($emailHeader['subject'] ?? '')));
break;
case 'date_entered':
$db = DBManagerFactory::getInstance();

View file

@ -42,6 +42,7 @@ if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Token\AccessTokenInterface;
interface ExternalOAuthProviderConnectorInterface
@ -142,4 +143,12 @@ interface ExternalOAuthProviderConnectorInterface
* @return string
*/
public function getAccessTokenRequestGrant(array $providerConfig): string;
/**
* Get Provider
* @param string $requestClientId
* @param string $requestClientSecret
* @return AbstractProvider|null
*/
public function getProvider(string $requestClientId, string $requestClientSecret): ?AbstractProvider;
}

View file

@ -268,6 +268,59 @@ class OAuthAuthorizationService
];
}
public function refreshExpiredOAuthToken(string $oAuthConnectionId): void
{
/** @var ExternalOAuthConnection $oauthConnection */
$oauthConnection = BeanFactory::getBean('ExternalOAuthConnection', $oAuthConnectionId);
$hasExpiredFeedback = $this->hasConnectionTokenExpired($oauthConnection);
$refreshToken = $hasExpiredFeedback['refreshToken'] ?? false;
if ($refreshToken === true) {
$refreshTokenFeedback = $this->refreshConnectionToken($oauthConnection);
if ($refreshTokenFeedback['success'] === false) {
$message = $this->getOAuthRefreshTokenErrorMessage(
$refreshTokenFeedback['reLogin'],
$oauthConnection,
$oAuthConnectionId
);
displayAdminError($message);
}
}
}
/**
* Get refersh token error messages
* @param $reLogin
* @param ExternalOAuthConnection $oauthConnection
* @param string $oAuthConnectionId
* @return string
*/
public function getOAuthRefreshTokenErrorMessage(
$reLogin,
ExternalOAuthConnection $oauthConnection,
string $oAuthConnectionId
): string {
$message = translate('ERR_OAUTH_CONNECTION_ERROR');
$linkAction = 'DetailView';
if ($reLogin === true) {
$linkAction = 'EditView';
$message = translate('WARN_OAUTH_TOKEN_SESSION_EXPIRED');
}
$oauthConnectionName = $oauthConnection->name;
$hasAccess = $oauthConnection->ACLAccess('edit') ?? false;
if ($hasAccess === true) {
$message .= " <a href=\"index.php?module=ExternalOAuthConnection&action=$linkAction&record=$oAuthConnectionId\">$oauthConnectionName</a>.";
} else {
$message .= $oauthConnectionName . '.';
}
return $message;
}
/**
* Map token to bean fields array
* @param string $providerId

View file

@ -73,6 +73,7 @@ class ExternalOAuthProvider extends Basic
public $expires_in_mapping;
public $refresh_token_mapping;
public $token_type_mapping;
public $redirect_uri_type;
/**
@ -322,10 +323,13 @@ class ExternalOAuthProvider extends Basic
global $sugar_config;
$siteUrl = $sugar_config['site_url'] ?? '';
$siteUrl = str_ireplace('index.php', '', (string) $siteUrl);
$siteUrl = rtrim($siteUrl, " \t\n\r\0\x0B\/");
if ($this->redirect_uri_type === 'pretty_url') {
return $siteUrl . '/ep/setExternalOAuthToken';
}
return $siteUrl . '/index.php?entryPoint=setExternalOAuthToken';
}

View file

@ -0,0 +1,196 @@
/**
* SuiteCRM is a customer relationship management program developed by SuiteCRM Ltd.
* Copyright (C) 2025 SuiteCRM 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 SUITECRM, SUITECRM 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/>.
*
* 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
* "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 "Supercharged by SuiteCRM".
*/
var externalOAuthProviderFields = function () {
var getDefaultFieldGetter = function () {
return function (field$) {
return (field$ && field$.val()) || '';
};
};
var getFunctionFieldGetter = function () {
return function (field$) {
field$ = field$[0];
if (!field$) {
return '';
}
return field$.innerText || field$.textContent || '';
};
};
var getDefaultFieldSetter = function () {
return function (field$, value) {
if (!field$) {
return;
}
field$.val(value);
field$.change();
};
};
var getFunctionFieldSetter = function () {
return function (field$, value) {
field$ = field$[0];
if (!field$) {
return;
}
field$.innerText = value || '';
};
};
return {
fields: {
'record': {
type: 'varchar',
getField$: function (field) {
return $('input[name=' + field + ']') || null;
}
},
'name': {
type: 'varchar'
},
'client_id': {
type: 'varchar'
},
'client_secret': {
type: 'varchar'
},
'redirect_uri_span': {
type: 'function'
},
'redirect_uri_type': {
type: 'enum'
},
},
getters: {
default: getDefaultFieldGetter(),
varchar: getDefaultFieldGetter(),
function: getFunctionFieldGetter(),
checkbox: function (field$) {
return (field$ && field$.prop('checked')) || false;
}
},
setters: {
default: getDefaultFieldSetter(),
varchar: getDefaultFieldSetter(),
function: getFunctionFieldSetter(),
checkbox: function (field$, value) {
if (!field$) {
return;
}
field$.prop('checked', !!value);
}
},
setValue: function (field, value) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
var setter = this.getValueSetter(field);
if (!setter) {
return null;
}
return setter(field$, value);
},
getValue: function (field) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
var getter = this.getValueGetter(field);
if (!getter) {
return null;
}
return getter(field$);
},
hide: function (field) {
var field$ = this.getFieldCell$(field);
if (!field$ || !field$.length) {
return;
}
field$.hide();
},
show: function (field) {
var field$ = this.getFieldCell$(field);
if (!field$ || !field$.length) {
return;
}
field$.show();
},
getField$: function (field) {
var handler = (this.fields[field] && this.fields[field].getField$) || null;
if (handler) {
return handler(field);
}
return $('#' + field) || null;
},
getFieldCell$: function (field) {
return $('[data-field="' + field + '"]') || null;
},
getFieldType: function (field) {
return (this.fields[field] && this.fields[field].type) || 'varchar';
},
getValueGetter: function (field) {
var type = this.getFieldType(field);
return this.getters[type] || this.getters['default'];
},
getValueSetter: function (field) {
var type = this.getFieldType(field);
return this.setters[type] || this.setters['default'];
}
};
}();

View file

@ -0,0 +1,57 @@
/**
* SuiteCRM is a customer relationship management program developed by SuiteCRM Ltd.
* Copyright (C) 2025 SuiteCRM 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 SUITECRM, SUITECRM 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/>.
*
* 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
* "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 "Supercharged by SuiteCRM".
*/
function toggleRedirectUri(type, url) {
var fieldValues = {
'query_string': {
'uri': 'index.php?entryPoint=setExternalOAuthToken'
},
'pretty_url': {
'uri': 'ep/setExternalOAuthToken'
}
}
var match = url.match(/^(.*?)(?:index\.php|ep)(?:\/|\?|$)/);
match = match ? match[1] : url;
var uri = fieldValues[type] ? fieldValues[type].uri : '';
externalOAuthProviderFields.setValue('redirect_uri_span', match + uri);
}
$(document).ready(function () {
var url = externalOAuthProviderFields.getValue('redirect_uri_span');
var type = externalOAuthProviderFields.getValue('redirect_uri_type');
toggleRedirectUri(type, url);
externalOAuthProviderFields.getField$('redirect_uri_type').change(function () {
url = externalOAuthProviderFields.getValue('redirect_uri_span');
type = externalOAuthProviderFields.getValue('redirect_uri_type');
toggleRedirectUri(type, url);
});
});

View file

@ -76,6 +76,7 @@ $mod_strings = [
'LBL_TYPE' => 'Type',
'LBL_CONNECTOR' => 'Connector',
'LBL_REDIRECT_URI' => 'Redirect URI',
'LBL_REDIRECT_URI_TYPE' => 'Redirect URI Type',
'LBL_CLIENT_ID' => 'Client Id',
'LBL_CLIENT_SECRET' => 'Client Secret',

View file

@ -95,7 +95,7 @@ $viewdefs[$module_name]['DetailView'] = [
],
[
'redirect_uri',
''
'redirect_uri_type'
],
],
'lbl_extra' => [

View file

@ -68,6 +68,13 @@ $viewdefs['ExternalOAuthProvider'] = [
'panelDefault' => 'expanded',
],
],
'javascript' => '
<script type="text/javascript">
{suite_combinescripts
files="modules/ExternalOAuthProvider/js/fields.js,
modules/ExternalOAuthProvider/js/redirect_uri_toggle.js"}
</script>
'
],
'panels' => [
'default' => [
@ -93,7 +100,7 @@ $viewdefs['ExternalOAuthProvider'] = [
],
[
'redirect_uri',
''
'redirect_uri_type'
],
],
'lbl_extra' => [

View file

@ -127,6 +127,21 @@ $dictionary['ExternalOAuthProvider'] = [
'exportable' => false,
'unified_search' => false,
],
'redirect_uri_type' => [
'name' => 'redirect_uri_type',
'vname' => 'LBL_REDIRECT_URI_TYPE',
'type' => 'enum',
'default' => 'pretty_url',
'options' => 'redirect_uri_type_dom',
'len' => 50,
'required' => false,
'reportable' => false,
'massupdate' => false,
'inline_edit' => false,
'importable' => false,
'exportable' => false,
'unified_search' => false,
],
'client_id' => [
'name' => 'client_id',
'vname' => 'LBL_CLIENT_ID',
@ -185,6 +200,7 @@ $dictionary['ExternalOAuthProvider'] = [
'vname' => 'LBL_AUTHORIZE_URL_OPTIONS',
'type' => 'stringmap',
'dbType' => 'text',
'default' => '{"prompt":"login"}',
'show_keys' => true,
'required' => false,
'reportable' => false,

View file

@ -52,7 +52,7 @@ require_once('modules/Import/ImportDuplicateCheck.php');
class Importer
{
/**
* @var ImportFieldSanitizer
* @var ImportFieldSanitize
*/
protected $ifs;
@ -62,27 +62,27 @@ class Importer
protected $defaultUserCurrency;
/**
* @var importColumns
* @var array
*/
protected $importColumns;
/**
* @var importSource
* @var ImportDataSource
*/
protected $importSource;
/**
* @var $isUpdateOnly
* @var bool
*/
protected $isUpdateOnly;
/**
* @var $bean
* @var SugarBean
*/
protected $bean;
/**
* @var sugarToExternalSourceFieldMap
* @var array
*/
protected $sugarToExternalSourceFieldMap = array();
@ -97,7 +97,7 @@ class Importer
$this->bean = $bean;
// use our own error handler
set_error_handler(array('Importer','handleImportErrors'), E_ALL);
set_error_handler(array('Importer','handleImportErrors'), E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE);
// Increase the max_execution_time since this step can take awhile
ini_set("max_execution_time", max($sugar_config['import_max_execution_time'], 3600));

View file

@ -130,7 +130,7 @@ abstract class ImportDataSource implements Iterator
* @return void
*/
abstract public function getHeaderColumns();
/**
* Set the source name.
*
@ -197,7 +197,7 @@ abstract class ImportDataSource implements Iterator
if ($module == 'Case') {
$module = 'aCase';
}
$last_import->bean_type = $module;
$last_import->bean_id = $id;
return $last_import->save();
@ -228,9 +228,9 @@ abstract class ImportDataSource implements Iterator
{
global $current_language;
$mod_strings = return_module_language($current_language, 'Import');
return "<b>{$mod_strings['LBL_ERROR']}</b> $error <br/>".
"<b>{$mod_strings['LBL_FIELD_NAME']}</b> $fieldName <br/>" .
"<b>{$mod_strings['LBL_VALUE']}</b> $fieldValue <br/>";
return "{$mod_strings['LBL_ERROR']}: $error. ".
"- {$mod_strings['LBL_FIELD_NAME']}: $fieldName. " .
"- {$mod_strings['LBL_VALUE']}: $fieldValue";
}
public function resetRowErrorCounter()
{
@ -330,7 +330,7 @@ abstract class ImportDataSource implements Iterator
//Add the error message to the first column
array_unshift($rowData, $errorMessage);
fputcsv($fp, $rowData);
fclose($fp);
fclose($fpNoErrors);
}

View file

@ -47,12 +47,11 @@
{
padding-left: 0px;
}
div.resultsTable {
overflow: auto;
width: 100%;
padding-top: 20px;
position: relative;
}
div.resultsTable {
overflow: auto;
padding-top: 20px;
position: relative;
}
</style>
{/literal}
@ -98,7 +97,7 @@
</form>
<br/>
<table width="100%" id="tabListContainerTable" cellpadding="0" cellspacing="0" border="0">
<tr>
<td nowrap id="tabListContainerTD">

View file

@ -80,7 +80,7 @@
{counter start=0 name="colCounter" print=false assign="colCounter"}
{foreach from=$rowData key=col item=params}
{strip}
<td align='left' valign="top">
<td align='left' valign="top" scope="record">
{$params}
</td>
{/strip}

View file

@ -128,7 +128,7 @@ class ImportListView
$this->ss->assign('navStrings', $navStrings);
$this->ss->assign('pageData', $this->generatePaginationData());
$this->ss->assign('tableID', $this->tableID);
$this->ss->assign('colCount', count($this->headerColumns));
$this->ss->assign('colCount', is_countable($this->headerColumns) ? count($this->headerColumns) : 0);
$this->ss->assign('APP', $app_strings);
$this->ss->assign('rowColor', array('oddListRow', 'evenListRow'));
$this->ss->assign('displayColumns', $this->headerColumns);

View file

@ -215,7 +215,7 @@ ProcessImport = new function()
+ "&import_module={$_REQUEST['import_module']}"
+ "&has_header=" + document.getElementById("importstepdup").has_header.value ;
if ( ProcessImport.fileCount >= ProcessImport.fileTotal ) {
YAHOO.SUGAR.MessageBox.updateProgress(1,'{$mod_strings['LBL_IMPORT_COMPLETED']}');
YAHOO.SUGAR.MessageBox.updateProgress(100,'{$mod_strings['LBL_IMPORT_COMPLETED']}');
SUGAR.util.hrefURL(locationStr);
}
else {
@ -238,7 +238,7 @@ ProcessImport = new function()
);
var move = 0;
if ( this.fileTotal > 0 ) {
move = this.fileCount/this.fileTotal;
move = (this.fileCount/this.fileTotal) * 100;
}
YAHOO.SUGAR.MessageBox.updateProgress( move,
"{$mod_strings['LBL_IMPORT_RECORDS']} " + ((this.fileCount * this.recordThreshold) + 1)

View file

@ -150,7 +150,7 @@ class AOPInboundEmail extends InboundEmail
$GLOBALS['log']->debug('InboundEmail created one case with number: '.$c->case_number);
$createCaseTemplateId = $this->get_stored_options('create_case_email_template', "");
if (!empty($this->stored_options)) {
$storedOptions = unserialize(base64_decode($this->stored_options));
$storedOptions = unserialize(base64_decode($this->stored_options), ['allowed_classes' => false]);
}
if (!empty($createCaseTemplateId)) {
$fromName = "";

View file

@ -1572,7 +1572,7 @@ class InboundEmail extends SugarBean
flush();
} // while
fclose($fh);
$diff = unserialize($data);
$diff = unserialize($data, ['allowed_classes' => false]);
if (!empty($diff)) {
if ((is_countable($diff) ? count($diff) : 0) > 50) {
$newDiff = array_slice($diff, 50, is_countable($diff) ? count($diff) : 0, true);
@ -2035,7 +2035,7 @@ class InboundEmail extends SugarBean
flush();
} // while
fclose($fh);
$results = unserialize($data);
$results = unserialize($data, ['allowed_classes' => false]);
} // if
} // if
if (!$cacheDataExists) {
@ -2964,7 +2964,7 @@ class InboundEmail extends SugarBean
$focusUser = BeanFactory::newBean('Users');
$focusUser->retrieve($groupId);
$mailerId = (isset($_REQUEST['outbound_email'])) ? $_REQUEST['outbound_email'] : "";
$mailerId = (isset($_REQUEST['outbound_email'])) ? $this->db->quote($_REQUEST['outbound_email']) : "";
$oe = new OutboundEmail();
if ($mailerId != "") {
@ -4144,14 +4144,14 @@ class InboundEmail extends SugarBean
$emailMessage = $this->mailParser->parse($emailBody, false)->getTextContent();
$emailMessage = $this->handleInlineImages($emailBody, $emailMessage);
$emailMessage = $this->customGetMessageText($emailMessage);
return SugarCleaner::cleanHtml($emailMessage, false);
return html_entity_decode(purify_html(SugarCleaner::cleanHtml($emailMessage, false)));
}
$emailMessage = $this->mailParser->parse($emailBody, false)->getHtmlContent();
$emailMessage = $this->handleInlineImages($emailBody, $emailMessage);
$emailMessage = $this->customGetMessageText($emailMessage);
$emailMessage = $this->customGetMessageText($emailMessage) ?? '';
return SugarCleaner::cleanHtml($emailMessage, $clean_email);
return html_entity_decode(purify_html(SugarCleaner::cleanHtml($emailMessage, $clean_email)));
}
/**
@ -5495,7 +5495,7 @@ class InboundEmail extends SugarBean
//// ASSIGN APPROPRIATE ATTRIBUTES TO NEW EMAIL OBJECT
// handle UTF-8/charset encoding in the ***headers***
$email->name = $this->handleMimeHeaderDecode($header->subject);
$email->name = purify_html($this->handleMimeHeaderDecode($parsedFullHeader->subject));
$email->type = 'inbound';
if (!empty($unixHeaderDate)) {
$email->date_sent_received = $timedate->asUser($unixHeaderDate);
@ -5713,7 +5713,7 @@ class InboundEmail extends SugarBean
$fullHeader = $this->getImap()->fetchHeader($uid, FT_UID);
$parsedFullHeader = $this->getImap()->rfc822ParseHeaders($fullHeader);
$email->name = $this->handleMimeHeaderDecode($parsedFullHeader->subject);
$email->name = purify_html($this->handleMimeHeaderDecode($parsedFullHeader->subject));
$email->type = 'inbound';
if (isset($request['metadata']['viewdefs'])) {
@ -7509,7 +7509,7 @@ class InboundEmail extends SugarBean
include($cache); // profides $cacheFile
/** @var $cacheFile array */
$metaOut = unserialize($cacheFile['out']);
$metaOut = unserialize($cacheFile['out'], ['allowed_classes' => false]);
$meta = $metaOut['meta']['email'];
$email = BeanFactory::newBean('Emails');
@ -8667,38 +8667,6 @@ eoq;
return 'imap2';
}
/**
* Get refersh token error messages
* @param $reLogin
* @param ExternalOAuthConnection $oauthConnection
* @param string $oAuthConnectionId
* @return string
*/
protected function getOAuthRefreshTokenErrorMessage(
$reLogin,
ExternalOAuthConnection $oauthConnection,
string $oAuthConnectionId
): string {
$message = translate('ERR_IMAP_OAUTH_CONNECTION_ERROR', 'InboundEmail');
$linkAction = 'DetailView';
if ($reLogin === true) {
$linkAction = 'EditView';
$message = translate('WARN_OAUTH_TOKEN_SESSION_EXPIRED', 'InboundEmail');
}
$oauthConnectionName = $oauthConnection->name;
$hasAccess = $oauthConnection->ACLAccess('edit') ?? false;
if ($hasAccess === true) {
$message .= " <a href=\"index.php?module=ExternalOAuthConnection&action=$linkAction&record=$oAuthConnectionId\">$oauthConnectionName</a>.";
} else {
$message .= $oauthConnectionName . '.';
}
return $message;
}
/**
* Get OAuthToken. Refresh if needed
* @param string $oAuthConnectionId
@ -8709,29 +8677,11 @@ eoq;
require_once __DIR__ . '/../ExternalOAuthConnection/services/OAuthAuthorizationService.php';
$oAuth = new OAuthAuthorizationService();
$oAuth->refreshExpiredOAuthToken($oAuthConnectionId);
/** @var ExternalOAuthConnection $oauthConnection */
$oauthConnection = BeanFactory::getBean('ExternalOAuthConnection', $oAuthConnectionId);
$password = $oauthConnection->access_token;
$hasExpiredFeedback = $oAuth->hasConnectionTokenExpired($oauthConnection);
$refreshToken = $hasExpiredFeedback['refreshToken'] ?? false;
if ($refreshToken === true) {
$refreshTokenFeedback = $oAuth->refreshConnectionToken($oauthConnection);
if ($refreshTokenFeedback['success'] === false) {
$message = $this->getOAuthRefreshTokenErrorMessage(
$refreshTokenFeedback['reLogin'],
$oauthConnection,
$oAuthConnectionId
);
displayAdminError($message);
return null;
}
return $oauthConnection->access_token;
}
return $password;
return $oauthConnection->access_token;
}
/**

View file

@ -167,7 +167,12 @@ if ($type === 'bounce') {
if (!empty($_REQUEST['external_oauth_connection_id'])) {
$externalOauthConnection = BeanFactory::getBean('ExternalOAuthConnection', $_REQUEST['external_oauth_connection_id']);
if ($externalOauthConnection->type !== $focus->type) {
$focusType = $focus->type ?? '';
if ($focusType === 'bounce') {
$focusType = 'group';
}
if ($externalOauthConnection->type !== $focusType) {
SugarApplication::appendErrorMessage($mod_strings['LBL_TYPE_DIFFERENT']);
SugarApplication::redirect('index.php?module=InboundEmail&action=EditView&is_personal=1&type=personal');
return;

View file

@ -37,7 +37,7 @@
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
function authTypetoggleFields(type) {
function authTypeToggleFields(type) {
var fieldsPerType = {
'basic': {
@ -81,10 +81,10 @@ function authTypetoggleFields(type) {
$(document).ready(function () {
var type = inboundEmailFields.getValue('auth_type');
authTypetoggleFields(type);
authTypeToggleFields(type);
inboundEmailFields.getField$('auth_type').change(function () {
type = inboundEmailFields.getValue('auth_type');
authTypetoggleFields(type);
authTypeToggleFields(type);
});
});

View file

@ -8,7 +8,7 @@ if (!defined('sugarEntry') || !sugarEntry) {
* 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.
* Copyright (C) 2011 - 2024 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
@ -119,17 +119,19 @@ class StandardField extends DynamicField
$this->baseField = get_widget($field->type) ;
foreach ($field->vardef_map as $property => $fmd_col) {
if ($property == "action" || $property == "label_value" || $property == "label"
|| ((substr((string) $property, 0, 3) == 'ext' && strlen((string) $property) == 4))
|| ((str_starts_with((string)$property, 'ext') && strlen((string) $property) == 4))
// possible bug here... $property is often the same as $fmd_col, but not always. Maybe we should also add:
// || ((str_starts_with((string)$fmd_col, 'ext') && strlen((string) $fmd_col) == 4))
// ... but a thorough analysis of the consequences of this would be required.
) {
continue;
}
// Bug 37043 - Avoid writing out vardef defintions that are the default value.
// Avoid writing out vardef definitions that are the default value, when possible.
// Since isDefaultvalue() is quite limited, and doesn't handle all cases well,
// sometimes we won't detect defaults and will store them anyway.
if (isset($newDef[$property]) &&
(
(!isset($currdef[$property]) && !$this->isDefaultValue($property, $newDef[$property], $this->baseField))
|| (isset($currdef[$property]) && $currdef[$property] != $newDef[$property])
)
!$this->isDefaultValue($property, $newDef[$property], $this->baseField)
) {
$this->custom_def[$property] =
is_string($newDef[$property]) ? htmlspecialchars_decode($newDef[$property], ENT_QUOTES) : $newDef[$property];

View file

@ -262,7 +262,7 @@ class ActivitiesRelationship extends OneToManyRelationship
'order' => 20 ,
'sort_order' => 'desc' ,
'sort_by' => 'date_modified' ,
'title_key' => 'LBL_HISTORY' ,
'title_key' => 'LBL_HISTORY_SUBPANEL_TITLE' ,
'type' => 'collection' ,
'subpanel_name' => 'history' , //this values is not associated with a physical file.
'module' => 'History' ,

View file

@ -461,7 +461,7 @@ abstract class AbstractMetaDataImplementation
// BEGIN ASSERTIONS
if ($type != MB_BASEMETADATALOCATION && $type != MB_HISTORYMETADATALOCATION) {
// just warn rather than die
$GLOBALS ['log']->warning(
$GLOBALS ['log']->warn(
"UndeployedMetaDataImplementation->getFileName(): view type $type is not recognized"
);
}

View file

@ -78,8 +78,7 @@ class TabController
$trimmed_tabs = trim($tabs);
//make sure serialized string is not empty
if (!empty($trimmed_tabs)) {
$tabs = base64_decode($tabs);
$tabs = unserialize($tabs);
$tabs = unserialize(base64_decode($tabs), ['allowed_classes' => false]);
//Ensure modules saved in the prefences exist.
foreach ($tabs as $id => $tab) {
if (!in_array($tab, $moduleList)) {

View file

@ -66,6 +66,13 @@ class OutboundEmailAccounts extends OutboundEmailAccounts_sugar
*/
public $mail_smtpuser;
public $mail_smtpauth_req;
public $mail_smtpssl;
public $mail_smtpport;
public $mail_smtpserver;
public $mail_smtptype;
public $mail_sendtype;
/**
* @var string
*/
@ -86,6 +93,9 @@ class OutboundEmailAccounts extends OutboundEmailAccounts_sugar
*/
public $reply_to_name;
public $auth_type = 'no_auth'; // 'no_auth', 'basic', 'oauth'
public $external_oauth_connection_id = '';
public function __construct()
{
parent::__construct();
@ -113,11 +123,34 @@ class OutboundEmailAccounts extends OutboundEmailAccounts_sugar
}
$this->mail_smtppass = $this->mail_smtppass ? blowfishEncode(blowfishGetKey('OutBoundEmail'), $this->mail_smtppass) : null;
if ($this->auth_type === 'basic') {
$this->mail_smtpauth_req = 1;
$this->external_oauth_connection_id = '';
}
if ($this->auth_type === 'no_auth') {
$this->mail_smtppass = '';
$this->mail_smtpauth_req = 0;
$this->external_oauth_connection_id = '';
}
if ($this->auth_type === 'oauth') {
$this->mail_smtppass = '';
$this->mail_smtpauth_req = 0;
}
$this->smtp_from_name = trim($this->smtp_from_name);
$this->smtp_from_addr = trim($this->smtp_from_addr);
$this->mail_smtpserver = trim($this->mail_smtpserver);
$this->mail_smtpuser = trim($this->mail_smtpuser);
if ($this->type === 'system') {
/** @var Administration $admin */
$admin = BeanFactory::newBean('Administration');
$admin->saveSetting('notify', 'fromname', $this->smtp_from_name);
$admin->saveSetting('notify', 'fromaddress', $this->smtp_from_addr);
}
$results = parent::save($check_notify);
return $results;
}
@ -134,6 +167,10 @@ class OutboundEmailAccounts extends OutboundEmailAccounts_sugar
return null;
}
if (isTrue($this->mail_smtpauth_req) && $this->auth_type === 'no_auth') {
$this->auth_type = 'basic';
}
$this->mail_smtppass = $this->mail_smtppass ? blowfishDecode(blowfishGetKey('OutBoundEmail'), $this->mail_smtppass) : null;
return $results;
}
@ -262,7 +299,9 @@ class OutboundEmailAccounts extends OutboundEmailAccounts_sugar
{
global $current_user;
$isNotAllowAction = $this->isNotAllowedAction($view);
$view = $view ?? '';
$isNotAllowAction = $this->isNotAllowedAction($view ?? '');
if ($isNotAllowAction === true) {
return false;
}
@ -406,6 +445,7 @@ HTML;
$adminNotifyFromAddress = $admin->settings['notify_fromaddress'];
isValidEmailAddress($adminNotifyFromAddress);
$adminNotifyFromName = $admin->settings['notify_fromname'];
$record = $_REQUEST['record'] ?? '';
$html = <<<HTML
<input id="sendTestOutboundEmailSettingsBtn" type="button" class="button" value="{$APP['LBL_EMAIL_TEST_OUTBOUND_SETTINGS']}" onclick="testOutboundSettings();">
<script type="text/javascript" src="cache/include/javascript/sugar_grp_yui_widgets.js"></script>
@ -431,6 +471,15 @@ HTML;
};
function testOutboundSettingsDialog() {
var notifyFromAddress = document.getElementById('smtp_from_addr') && document.getElementById('smtp_from_addr').value;
var notifyFromName = document.getElementById('smtp_from_name') && document.getElementById('smtp_from_name').value;
if (!notifyFromAddress || !notifyFromName) {
overlay("{$APP['ERR_INVALID_REQUIRED_FIELDS']}", "{$APP['LBL_EMAIL_SETTINGS_FROM_ADDR_NOT_SET']}", 'alert');
return;
}
// lazy load dialogue
if(!EmailMan.testOutboundDialog) {
EmailMan.testOutboundDialog = new YAHOO.widget.Dialog("testOutboundDialog", {
@ -507,7 +556,9 @@ HTML;
var smtpServer = document.getElementById('mail_smtpserver').value;
var smtpPort = document.getElementById('mail_smtpport').value;
var smtpssl = document.getElementById('mail_smtpssl').value;
var mailsmtpauthreq = document.getElementById('mail_smtpauth_req');
var authType = document.getElementById('auth_type').value || 'no_auth';
var externalOauthConnectionId = document.getElementById('external_oauth_connection_id').value || '';
var smtpPass = trim(document.getElementById('mail_smtppass').value);
var mail_sendtype = 'SMTP';
var adminNotifyFromAddress = document.getElementById('smtp_from_addr').value ? document.getElementById('smtp_from_addr').value :'$adminNotifyFromName';
var adminNotifyFromName = document.getElementById('smtp_from_name').value ? document.getElementById('smtp_from_name').value : '$adminNotifyFromAddress';
@ -516,9 +567,12 @@ HTML;
'mail_sendtype=' + mail_sendtype + '&' +
'mail_smtpserver=' + smtpServer + "&" +
"mail_smtpport=" + smtpPort + "&mail_smtpssl=" + smtpssl + "&" +
"mail_smtpauth_req=" + mailsmtpauthreq.checked + "&" +
"mail_auth_type=" + authType + "&" +
"mail_external_oauth_connection_id=" + externalOauthConnectionId + "&" +
"mail_smtpauth_req=" + (authType === 'basic' ? 1 : 0) + "&" +
"mail_smtpuser=" + trim(document.getElementById('mail_smtpuser').value) + "&" +
"mail_smtppass=" + trim(document.getElementById('mail_smtppass').value) + "&" +
"mail_smtppass=" + smtpPass + "&" +
"record=" + '$record' + "&" +
"outboundtest_to_address=" + toAddress + '&' +
'outboundtest_from_address=' + adminNotifyFromAddress + '&' +
'mail_from_name=' + adminNotifyFromName;

View file

@ -51,6 +51,32 @@ class OutboundEmailAccountsController extends SugarController
$this->bean->type = $type;
}
if (empty($this->bean) && $type === 'system' && !is_admin($GLOBALS['current_user'])) {
$this->hasAccess = false;
$this->view = 'noaccess';
return;
}
$oe = new OutboundEmail();
$oe = $oe->getSystemEmail();
if (empty($this->bean->id) && $type === 'system' && $oe !== null) {
$this->hasAccess = false;
$this->view = 'errors';
$this->errors = [
translate('LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_EXISTS', 'OutboundEmailAccounts'),
];
return;
}
if ($type === 'system' && $oe !== null && $oe->id !== $this->bean->id) {
$this->hasAccess = false;
$this->view = 'errors';
$this->errors = [
translate('LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_EXISTS', 'OutboundEmailAccounts'),
];
return;
}
if (empty($_REQUEST['record']) && $type === 'user') {
$this->hasAccess = true;
return;
@ -62,7 +88,7 @@ class OutboundEmailAccountsController extends SugarController
}
public function action_save() {
global $current_user;
global $current_user, $mod_strings;
$isNewRecord = (empty($this->bean->id) || $this->bean->new_with_id);
$this->bean->mail_sendtype = 'SMTP';
@ -84,6 +110,14 @@ class OutboundEmailAccountsController extends SugarController
}
}
$oe = new OutboundEmail();
$oe = $oe->getSystemEmail();
$type = $this->bean->type;
if ($type === 'user'){
$type = 'personal';
}
if ($isNewRecord && empty($this->bean->user_id)) {
$this->bean->user_id = $current_user->id;
$this->bean->assigned_user_id = $current_user->id;
@ -93,6 +127,34 @@ class OutboundEmailAccountsController extends SugarController
$this->bean->assigned_user_id = $this->bean->user_id;
}
$authType = $_REQUEST['auth_type'] ?? '';
$oauth = null;
if ($authType === 'oauth' || ($_REQUEST['auth_type'] ?? '') === 'oauth') {
$oauth = BeanFactory::getBean('ExternalOAuthConnection', $_REQUEST['external_oauth_connection_id']);
}
if ($type === 'system' && $oe !== null && $oe->id !== $this->bean->id) {
$this->hasAccess = false;
$this->view = 'errors';
$this->errors = [
translate('LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_EXISTS', 'OutboundEmailAccounts'),
];
return;
}
if ($type === 'system' && $oauth !== null && $oauth->type !== 'group') {
SugarApplication::appendErrorMessage($mod_strings['LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_IS_NOT_GROUP']);
SugarApplication::redirect('index.php?module=OutboundEmailAccounts&action=DetailView&record=' . $this->bean->id);
return;
}
if ($type !== 'system' && $oauth !== null && $oauth->type !== $type) {
SugarApplication::appendErrorMessage($mod_strings['LBL_ERROR_OUTBOUND_EMAIL_CONNECTION_TYPE_MISMATCH']);
SugarApplication::redirect('index.php?module=OutboundEmailAccounts&action=DetailView&record=' . $this->bean->id);
return;
}
parent::action_save();
}
}

View file

@ -0,0 +1,81 @@
/**
* SuiteCRM is a customer relationship management program developed by SuiteCRM Ltd.
* Copyright (C) 2025 SuiteCRM 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 SUITECRM, SUITECRM 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/>.
*
* 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
* "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 "Supercharged by SuiteCRM".
*/
function authTypeToggleFields(type) {
var fieldsPerType = {
'basic': {
'mail_smtppass': true,
'external_oauth_connection_name': false,
},
'no_auth': {
'mail_smtppass': false,
'external_oauth_connection_name': false,
},
'oauth': {
'mail_smtppass': false,
'external_oauth_connection_name': true,
},
};
var fieldType = {
'mail_smtppass': 'password',
'external_oauth_connection_name': 'relate',
};
var fieldDisplay = fieldsPerType[type] || fieldsPerType.personal;
Object.keys(fieldDisplay).forEach(function (fieldKey) {
var display = fieldDisplay[fieldKey];
var method = 'show';
var required = true;
if (!display) {
method = 'hide';
outboundEmailFields.setValue(fieldKey, '');
required = false;
}
var isValue = outboundEmailFields.getData(fieldKey, 'is-value-set');
if (isValue === true) {
required = false;
}
outboundEmailFields.setRequired(fieldKey, fieldType[fieldKey], 'EditView', required);
outboundEmailFields[method](fieldKey);
});
}
$(document).ready(function () {
var type = outboundEmailFields.getValue('auth_type');
authTypeToggleFields(type);
outboundEmailFields.getField$('auth_type').change(function () {
type = outboundEmailFields.getValue('auth_type');
authTypeToggleFields(type);
});
});

View file

@ -38,144 +38,256 @@
*/
var outboundEmailFields = function () {
var getDefaultFieldGetter = function () {
return function (field$) {
return (field$ && field$.val()) || '';
var requiredLabelTemplate = '<span class="required">*</span>';
var validationMessageTemplate = '<div class="required validation-message">Missing required field: $FIELD_NAME</div>';
var getValidationDefinition = function (formName, field) {
if (validate[formName]) {
for (i = 0; i < validate[formName].length; i++) {
if (validate[formName][i][nameIndex] == field) {
return validate[formName][i];
}
}
}
return null;
};
};
var getDefaultFieldSetter = function () {
return function (field$, value) {
if (!field$) {
return;
}
var configureValidation = function (formName, field, required) {
field$.val(value);
field$.change();
var definition = getValidationDefinition(formName, field);
if (!definition) {
return;
}
var isRequired = true;
if (!required) {
isRequired = false;
}
definition[requiredIndex] = isRequired;
};
};
return {
fields: {
'record': {
type: 'varchar',
var addRequiredIndicator = function ($label) {
var $indicator = $label.find('.required');
if ($indicator.length < 1) {
$label.append($(requiredLabelTemplate));
}
};
var removeRequiredIndicator = function ($label) {
var $indicator = $label.find('.required');
if ($indicator.length > 0) {
$indicator.remove();
}
};
var getDefaultFieldGetter = function () {
return function (field$) {
return (field$ && field$.val()) || '';
};
};
var getDefaultFieldSetter = function () {
return function (field$, value) {
if (!field$) {
return;
}
field$.val(value);
field$.change();
};
};
return {
fields: {
'record': {
type: 'varchar',
getField$: function (field) {
return $('input[name=' + field + ']') || null;
}
},
'mail_smtpssl': {
type: 'varchar'
},
'mail_smtppass': {
type: 'varchar',
},
'owner_name': {
type: 'varchar'
},
'type': {
type: 'varchar'
},
'mail_smtpport': {
type: 'varchar'
},
'mail_smtpauth_req': {
type: 'checkbox'
},
'auth_type': {
type: 'varchar'
},
'external_oauth_connection_name': {
type: 'varchar',
getField$: function (field) {
return $('input[name=' + field + ']') || null;
}
},
'external_oauth_connection_id': {
type: 'varchar',
getField$: function (field) {
return $('input[name=' + field + ']') || null;
}
},
},
getters: {
default: getDefaultFieldGetter(),
varchar: getDefaultFieldGetter(),
checkbox: function (field$) {
return (field$ && field$.prop('checked')) || false;
}
},
setters: {
default: getDefaultFieldSetter(),
varchar: getDefaultFieldSetter(),
checkbox: function (field$, value) {
if (!field$) {
return;
}
field$.prop('checked', !!value);
}
},
setValue: function (field, value) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
var setter = this.getValueSetter(field);
if (!setter) {
return null;
}
return setter(field$, value);
},
getValue: function (field) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
var getter = this.getValueGetter(field);
if (!getter) {
return null;
}
return getter(field$);
},
getData: function (field, dataKey) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
return field$.data(dataKey);
},
hide: function (field) {
var field$ = this.getFieldCell$(field);
if (!field$ || !field$.length) {
return;
}
field$.hide();
},
show: function (field) {
var field$ = this.getFieldCell$(field);
if (!field$ || !field$.length) {
return;
}
field$.show();
},
getField$: function (field) {
return $('input[name=' + field + ']') || null;
}
},
'mail_smtpssl': {
type: 'varchar'
},
'owner_name': {
type: 'varchar'
},
'type': {
type: 'varchar'
},
'mail_smtpport': {
type: 'varchar'
},
'mail_smtpauth_req': {
type: 'checkbox'
},
},
var handler = (this.fields[field] && this.fields[field].getField$) || null;
getters: {
default: getDefaultFieldGetter(),
varchar: getDefaultFieldGetter(),
checkbox: function (field$) {
return (field$ && field$.prop('checked')) || false;
}
},
if (handler) {
return handler(field);
}
setters: {
default: getDefaultFieldSetter(),
varchar: getDefaultFieldSetter(),
checkbox: function (field$, value) {
if (!field$) {
return;
}
return $('#' + field) || null;
},
field$.prop('checked', !!value);
}
},
getFieldCell$: function (field) {
return $('[data-field="' + field + '"]') || null;
},
setValue: function (field, value) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
getFieldType: function (field) {
return (this.fields[field] && this.fields[field].type) || 'varchar';
},
var setter = this.getValueSetter(field);
if (!setter) {
return null;
}
getValueGetter: function (field) {
var handler = (this.fields[field] && this.fields[field].getter) || null;
return setter(field$, value);
},
if (handler) {
return handler;
}
getValue: function (field) {
var field$ = this.getField$(field);
if (!field$) {
return null;
}
var type = this.getFieldType(field);
return this.getters[type] || this.getters['default'];
},
var getter = this.getValueGetter(field);
if (!getter) {
return null;
}
getValueSetter: function (field) {
var handler = (this.fields[field] && this.fields[field].setter) || null;
return getter(field$);
},
if (handler) {
return handler;
}
hide: function (field) {
var field$ = this.getFieldCell$(field);
var type = this.getFieldType(field);
return this.setters[type] || this.setters['default'];
},
if (!field$ || !field$.length) {
return;
}
setRequired: function (field, fieldType, formName, required) {
field$.hide();
},
var $editView = $('#EditView');
if (!$editView || !$editView.length) {
return;
}
configureValidation(this.formName, this.name, required);
show: function (field) {
var field$ = this.getFieldCell$(field);
this.setRequiredIndicator(field, required);
if (!field$ || !field$.length) {
return;
}
if (required) {
addToValidate(formName, field, fieldType, true, SUGAR.language.get('OutboundEmailAccounts', "LBL_" + field.toUpperCase()));
} else {
removeFromValidate(formName, field);
}
field$.show();
},
},
setRequiredIndicator: function (field, required) {
var $label = this.getFieldCell$(field).find('.label');
if (required) {
addRequiredIndicator($label);
} else {
removeRequiredIndicator($label);
}
},
getField$: function (field) {
var handler = (this.fields[field] && this.fields[field].getField$) || null;
if (handler) {
return handler(field);
}
return $('#' + field) || null;
},
getFieldCell$: function (field) {
return $('[data-field="' + field + '"]') || null;
},
getFieldType: function (field) {
return (this.fields[field] && this.fields[field].type) || 'varchar';
},
getValueGetter: function (field) {
var type = this.getFieldType(field);
return this.getters[type] || this.getters['default'];
},
getValueSetter: function (field) {
var type = this.getFieldType(field);
return this.setters[type] || this.setters['default'];
}
};
};
}();

View file

@ -1,76 +0,0 @@
/**
*
* 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 - 2022 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".
*/
function toggleAuthFields(authEnabled) {
var status = 'disabled';
if (authEnabled) {
status = 'enabled';
}
var fieldsPerStatus = {
'enabled': {
'mail_smtppass': true,
},
'disabled': {
'mail_smtppass': false,
},
};
var fieldDisplay = fieldsPerStatus[status] || fieldsPerStatus.disabled;
Object.keys(fieldDisplay).forEach(function (fieldKey) {
var display = fieldDisplay[fieldKey];
var method = 'show';
if(!display) {
method = 'hide';
}
outboundEmailFields[method](fieldKey);
});
}
$(document).ready(function () {
var authEnabled = outboundEmailFields.getValue('mail_smtpauth_req');
toggleAuthFields(authEnabled);
$('#mail_smtpauth_req').change(function () {
var authEnabled = outboundEmailFields.getValue('mail_smtpauth_req');
toggleAuthFields(authEnabled);
});
});

View file

@ -118,5 +118,14 @@ $mod_strings = array(
'LBL_OWNER' => 'Owner',
'LBL_OWNER_NAME' => 'Owner',
'LNK_EXTERNAL_OAUTH_CONNECTIONS' => 'External OAuth Connections'
'LBL_AUTH_TYPE' => 'Auth Type',
'LBL_EXTERNAL_OAUTH_CONNECTION' => 'External OAuth Connection',
'LBL_EXTERNAL_OAUTH_CONNECTION_ID' => 'External OAuth Connection id',
'LBL_EXTERNAL_OAUTH_CONNECTION_NAME' => 'External OAuth Connection',
'LNK_EXTERNAL_OAUTH_CONNECTIONS' => 'External OAuth Connections',
'LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_EXISTS' => 'System Outbound Email Account already exists. Please remove it before creating a new one.',
'LBL_ERROR_OUTBOUND_EMAIL_SYSTEM_IS_NOT_GROUP' => 'When configuring the System Outbound account using OAuth, you must select a Group-Type External Oauth Connection',
'LBL_ERROR_OUTBOUND_EMAIL_CONNECTION_TYPE_MISMATCH' => 'When configuring the Outbound account using OAuth, you must select a External Oauth Connection the same type as the Outbound Email Account (Group or Personal)',
);

View file

@ -37,7 +37,7 @@ $viewdefs ['OutboundEmailAccounts'] = [
files="modules/OutboundEmailAccounts/js/fields.js,
modules/OutboundEmailAccounts/js/owner_toggle.js,
modules/OutboundEmailAccounts/js/panel_toggle.js,
modules/OutboundEmailAccounts/js/smtp_auth_toggle.js"}
modules/OutboundEmailAccounts/js/auth_type_fields_toggle.js"}
</script>
',
],
@ -57,17 +57,21 @@ $viewdefs ['OutboundEmailAccounts'] = [
],
'lbl_connection_configuration' => [
[
'mail_smtpserver',
'mail_smtpauth_req',
],
[
'mail_smtpssl',
'auth_type',
'mail_smtpuser',
],
[
'mail_smtpport',
'mail_smtpserver',
'external_oauth_connection_name',
],
[
'mail_smtpssl',
'',
],
[
'mail_smtpport',
''
],
],
'lbl_outbound_configuration' => [
[

View file

@ -40,7 +40,7 @@ $viewdefs ['OutboundEmailAccounts'] = [
modules/OutboundEmailAccounts/js/ssl_port_set.js,
modules/OutboundEmailAccounts/js/panel_toggle.js,
modules/OutboundEmailAccounts/js/owner_toggle.js,
modules/OutboundEmailAccounts/js/smtp_auth_toggle.js"}
modules/OutboundEmailAccounts/js/auth_type_fields_toggle.js"}
</script>
',
],
@ -60,17 +60,26 @@ $viewdefs ['OutboundEmailAccounts'] = [
],
'lbl_connection_configuration' => [
[
'mail_smtpserver',
'mail_smtpauth_req',
],
[
'mail_smtpssl',
'auth_type',
'mail_smtpuser',
],
[
'mail_smtpport',
'mail_smtpserver',
'mail_smtppass',
],
[
'mail_smtpssl',
[
'name' => 'external_oauth_connection_name',
'displayParams' => [
'initial_filter' => '{if $fields.type.value === "system"}&type="+"group"+"{/if}',
],
],
],
[
'mail_smtpport',
'',
],
[
[
'name' => 'sent_test_email_btn',

Some files were not shown because too many files have changed in this diff Show more