From ba7632c609753ff807204bf8932d3e8046202151 Mon Sep 17 00:00:00 2001 From: Gyula Madarasz Date: Wed, 21 Mar 2018 18:54:40 +0000 Subject: [PATCH] Fixed #5548 - using token for confirm opt in email address --- include/EntryPointConfirmOptInHandler.php | 8 +- include/MVC/View/SugarView.php | 2 +- .../SugarEmailAddress/SugarEmailAddress.php | 86 ++++++++++++------- include/language/en_us.lang.php | 1 + .../suite_install/SystemEmailTemplates.php | 2 +- metadata/email_addressesMetaData.php | 7 ++ modules/Administration/repairDatabase.php | 2 +- modules/EmailMan/EmailMan.php | 8 +- modules/Emails/language/en_us.lang.php | 1 + 9 files changed, 75 insertions(+), 42 deletions(-) diff --git a/include/EntryPointConfirmOptInHandler.php b/include/EntryPointConfirmOptInHandler.php index ecd4380a415..941e104025c 100644 --- a/include/EntryPointConfirmOptInHandler.php +++ b/include/EntryPointConfirmOptInHandler.php @@ -152,11 +152,9 @@ private function methodConfirmOptInSelected($post) */ private function methodConfirmOptInUser($request) { $emailAddress = BeanFactory::getBean('EmailAddresses'); - $this->emailAddress = $emailAddress->retrieve_by_string_fields( - array( - 'email_address' => $request['from'] - ) - ); + $this->emailAddress = $emailAddress->retrieve_by_string_fields([ + 'confirm_opt_in_token' => $request['from'] + ]); if ($this->emailAddress) { $this->emailAddress->confirmOptIn(); diff --git a/include/MVC/View/SugarView.php b/include/MVC/View/SugarView.php index 068bcdfb2eb..b9d7629ae5a 100755 --- a/include/MVC/View/SugarView.php +++ b/include/MVC/View/SugarView.php @@ -663,7 +663,7 @@ public function displayHeader($retModTabs = false) } // put the current module at the top of the list - if (!empty($moduleTab)) { + if (!empty($moduleTab) && isset($tabData['modules'][$moduleTab])) { unset($topTabs[$moduleTab]); $topTabs = array_merge( array($moduleTab => $tabData['modules'][$moduleTab]), diff --git a/include/SugarEmailAddress/SugarEmailAddress.php b/include/SugarEmailAddress/SugarEmailAddress.php index 023682e7baf..2ab56b8a12e 100755 --- a/include/SugarEmailAddress/SugarEmailAddress.php +++ b/include/SugarEmailAddress/SugarEmailAddress.php @@ -162,9 +162,15 @@ class SugarEmailAddress extends SugarBean public $confirm_opt_in_sent_date; /** - * @var TimeDate $confirm_opt_in_sent_date + * @var TimeDate $confirm_opt_in_fail_date */ public $confirm_opt_in_fail_date; + + /** + * + * @var string + */ + public $confirm_opt_in_token; /** * @var array $doNotDisplayOptInTickForModule @@ -204,7 +210,7 @@ public function SugarEmailAddress() */ public function handleLegacySave($bean) { - if ($this->needsToParseLegacyAddresses($bean)){ + if ($this->needsToParseLegacyAddresses($bean)) { $this->parseLegacyEmailAddresses($bean); } $this->populateAddresses($bean->id, $bean->module_dir, array(), ''); @@ -610,7 +616,7 @@ public function saveEmail( ($checknotify) is not implemented. Please pass the correct arguments into SugarEmailAddress::saveEmail()'); } - if( + if ( empty($this->addresses) || $in_workflow === true ) { @@ -1045,7 +1051,7 @@ public function addAddress( $replyToFlag = ($replyTo) ? '1' : '0'; $invalidFlag = ($invalid) ? '1' : '0'; $optOutFlag = ($optOut) ? '1' : '0'; - if(!is_null($optIn)) { + if (!is_null($optIn)) { $optInFlag = ($optIn) ? '1' : '0'; } @@ -1072,7 +1078,7 @@ public function addAddress( 'confirm_opt_in_flag' => null, ); - if(!is_null($optIn)) { + if (!is_null($optIn)) { $addr['confirm_opt_in_flag'] = $optInFlag; } @@ -1242,7 +1248,7 @@ public function AddUpdateEmailAddress($addr, $invalid = 0, $opt_out = 0, $id = n } // confirmed opt in check - if(!is_null($optInFlag)) { + if (!is_null($optInFlag)) { $isValidEmailAddress = ($opt_out !== 1 && $invalid !== 1); $this->retrieve($id); $optInIndication = $this->getOptInStatus(); @@ -1251,7 +1257,7 @@ public function AddUpdateEmailAddress($addr, $invalid = 0, $opt_out = 0, $id = n && $this->isOptedInStatus($optInIndication) && (int)$optInFlag === 1 ) { - $new_confirmed_opt_in = $this->getConfirmedOptInState(); + $new_confirmed_opt_in = $this->getConfirmedOptInState(); } elseif ( $isValidEmailAddress && (int)$optInFlag === 1 @@ -1287,7 +1293,7 @@ public function AddUpdateEmailAddress($addr, $invalid = 0, $opt_out = 0, $id = n $duplicate->confirm_opt_in = $new_confirmed_opt_in; $upd_r = $this->db->query($upd_q); - if(!is_null($optInFlag)) { + if (!is_null($optInFlag)) { if ($new_confirmed_opt_in === self::COI_STAT_DISABLED) { // reset confirm opt in $upd_q = 'UPDATE ' . $this->table_name . ' ' . @@ -1303,7 +1309,7 @@ public function AddUpdateEmailAddress($addr, $invalid = 0, $opt_out = 0, $id = n } } - if(!empty($this->fetched_row)) { + if (!empty($this->fetched_row)) { foreach ($this->fetched_row as $fieldName => $fieldValue) { $this->{$fieldName} = $duplicate->{$fieldName}; } @@ -1333,7 +1339,8 @@ public function AddUpdateEmailAddress($addr, $invalid = 0, $opt_out = 0, $id = n /** * @return string */ - public function getConfirmedOptInState() { + public function getConfirmedOptInState() + { return $this->confirm_opt_in; } @@ -1640,7 +1647,7 @@ public function getEmailAddressWidgetDetailView($focus, $tpl = '') 'address' => $current_user->getEmailLink2($addressItem['email_address'], $focus) ); - if(empty($emailAddress['address'])) { + if (empty($emailAddress['address'])) { // Email Link is missing, lets just print the email address in plain text instead. $emailAddress['address'] = $addressItem['email_address']; } @@ -1931,22 +1938,22 @@ public function resetOptIn() * @return string|bool ID or false on failed * @throws RuntimeException this function updates an exists SugarEmailAddress bean should have ID */ - public function optIn() { - + public function optIn() + { if (!$this->id) { $msg = 'Trying to update opt-in email address without email address ID.'; LoggerManager::getLogger()->fatal($msg); throw new RuntimeException($msg); } - if(!$this->retrieve()) { + if (!$this->retrieve()) { $msg = 'Retrieve email address for opt-in failed.'; LoggerManager::getLogger()->fatal($msg); throw new RuntimeException($msg); } $state = $this->isConfirmedOptIn() ? self::COI_STAT_CONFIRMED_OPT_IN : self::COI_STAT_OPT_IN; - if(!$this->setConfirmedOptInState($state)) { + if (!$this->setConfirmedOptInState($state)) { $msg = 'set confirm opt in state of email address "' . $this->email_address . '" failed.'; LoggerManager::getLogger()->fatal($msg); throw new RuntimeException($msg); @@ -1963,7 +1970,8 @@ public function optIn() { * @param string $state * @return boolean */ - public function setConfirmedOptInState($state) { + public function setConfirmedOptInState($state) + { $this->confirm_opt_in = $state; $ret = parent::save(); return $ret; @@ -1978,8 +1986,8 @@ public function setConfirmedOptInState($state) { * @param string $moduleTab optional, using module name if null * @return array ViewDefs for Confirm Opt In action link */ - public static function getSendConfirmOptInEmailActionLinkDefs($module, $returnModule = null, $returnAction = null, $moduleTab = null) { - + public static function getSendConfirmOptInEmailActionLinkDefs($module, $returnModule = null, $returnAction = null, $moduleTab = null) + { $configurator = new Configurator(); $configOptInEnabled = $configurator->isConfirmOptInEnabled(); @@ -2038,7 +2046,8 @@ public static function getSendConfirmOptInEmailActionLinkDefs($module, $returnMo * Uses the configuration to determine opt in status * @return string */ - public function getOptInStatus() { + public function getOptInStatus() + { $configurator = new Configurator(); $enableConfirmedOptIn = $configurator->config['email_enable_confirm_opt_in']; $optInFromFlags = $this->getOptInIndicationFromFlags(); @@ -2116,7 +2125,7 @@ private function getOptInIndicationFromFlags() * @return bool true when the an confirm optin email was successfully sent * @throws Exception */ - private function isConfirmOptInEmailSent () + private function isConfirmOptInEmailSent() { if (empty($this->confirm_opt_in_sent_date)) { return false; @@ -2143,7 +2152,7 @@ private function isConfirmOptInEmailSent () * @return bool true when the an confirm optin email failed to send * @throws Exception */ - private function isConfirmOptInEmailFailed () + private function isConfirmOptInEmailFailed() { if (empty($this->confirm_opt_in_fail_date)) { return false; @@ -2169,7 +2178,7 @@ private function isConfirmOptInEmailFailed () /** * @return bool if confirm opt in email has not yet been sent */ - private function isConfirmOptInEmailNotSent () + private function isConfirmOptInEmailNotSent() { if ( empty($this->confirm_opt_in_sent_date) @@ -2254,7 +2263,6 @@ private function isOptedInStatus($emailAddressIndicatorStatus = self::COI_FLAG_N */ public function getOptInStatusTickHTML() { - global $app_strings; $configurator = new Configurator(); @@ -2262,13 +2270,13 @@ public function getOptInStatusTickHTML() $tickHtml = ''; - if(isset($sugar_config['email_enable_confirm_opt_in'])) { + if (isset($sugar_config['email_enable_confirm_opt_in'])) { $emailConfigEnableConfirmOptIn = $sugar_config['email_enable_confirm_opt_in']; - $template = new Sugar_Smarty(); + $template = new Sugar_Smarty(); - $optInStatus = $this->getOptInStatus(); - switch($optInStatus) { + $optInStatus = $this->getOptInStatus(); + switch ($optInStatus) { case self::COI_FLAG_OPT_IN: $optInFlagClass = 'email-opt-in-confirmed'; $optInFlagTitle = $app_strings['LBL_OPT_IN']; @@ -2312,15 +2320,27 @@ public function getOptInStatusTickHTML() break; } - $template->assign('optInFlagClass', $optInFlagClass); - $template->assign('optInFlagTitle', $optInFlagTitle); - $template->assign('optInFlagText', $optInFlagText); - $tickHtml = $template->fetch('include/SugarEmailAddress/templates/optInStatusTick.tpl'); - } + $template->assign('optInFlagClass', $optInFlagClass); + $template->assign('optInFlagTitle', $optInFlagTitle); + $template->assign('optInFlagText', $optInFlagText); + $tickHtml = $template->fetch('include/SugarEmailAddress/templates/optInStatusTick.tpl'); + } return $tickHtml; } - + + /** + * + * @return string + */ + public function getConfirmOptInTokenGenerateIfNotExists() + { + if (!$this->confirm_opt_in_token) { + $this->confirm_opt_in_token = md5(time()) . md5($this->email_address) . md5(rand(0, 9999999)) . md5(rand(0, 9999999)); + $this->save(); + } + return $this->confirm_opt_in_token; + } } // end class def require_once __DIR__.'/getEmailAddressWidget.php'; diff --git a/include/language/en_us.lang.php b/include/language/en_us.lang.php index 1ea0b648ddb..60b9cacdbc8 100755 --- a/include/language/en_us.lang.php +++ b/include/language/en_us.lang.php @@ -2091,6 +2091,7 @@ 'LBL_CONFIRM_OPT_IN_DATE' => 'Confirmed Opt In Date', 'LBL_CONFIRM_OPT_IN_SENT_DATE' => 'Confirmed Opt In Sent Date', 'LBL_CONFIRM_OPT_IN_FAIL_DATE' => 'Confirmed Opt In Fail Date', + 'LBL_CONFIRM_OPT_IN_TOKEN' => 'Confirm Opt In Token', 'ERR_OPT_IN_TPL_NOT_SET' => 'Opt In Email Template is not configured. Please set up in email settings.', 'ERR_OPT_IN_RELATION_INCORRECT' => 'Opt In requires the email to be related to Account/Contact/Lead/Target', diff --git a/install/suite_install/SystemEmailTemplates.php b/install/suite_install/SystemEmailTemplates.php index 6b06cbc780e..35ad481dd09 100644 --- a/install/suite_install/SystemEmailTemplates.php +++ b/install/suite_install/SystemEmailTemplates.php @@ -114,7 +114,7 @@ function getSystemEmailTemplates() '

Hi $contact_first_name $contact_last_name,

Please confirm that you have opted in by selecting the following link: - Opt In + Opt In

' ); diff --git a/metadata/email_addressesMetaData.php b/metadata/email_addressesMetaData.php index 645cdaec87a..9438b2376bc 100755 --- a/metadata/email_addressesMetaData.php +++ b/metadata/email_addressesMetaData.php @@ -111,6 +111,13 @@ 'vname' => 'LBL_CONFIRM_OPT_IN_FAIL_DATE', ), + 'confirm_opt_in_token' => [ + 'name' => 'confirm_opt_in_token', + 'type' => 'varchar', + 'len' => 255, + 'vname' => 'LBL_CONFIRM_OPT_IN_TOKEN', + ], + 'date_created' => array( 'name' => 'date_created', 'type' => 'datetime', diff --git a/modules/Administration/repairDatabase.php b/modules/Administration/repairDatabase.php index d6d53746b19..dc40af3904c 100755 --- a/modules/Administration/repairDatabase.php +++ b/modules/Administration/repairDatabase.php @@ -113,7 +113,7 @@ unset($GLOBALS['dictionary'][$bean]); $focus = new $bean (); if (($focus instanceOf SugarBean) && !isset($repairedTables[$focus->table_name])) { - $sql .= $db->repairTable($focus, $execute); + $sql .= $db->repairTable($focus, $execute); $repairedTables[$focus->table_name] = true; } //Repair Custom Fields diff --git a/modules/EmailMan/EmailMan.php b/modules/EmailMan/EmailMan.php index 0a687dc0930..cbbba805a35 100755 --- a/modules/EmailMan/EmailMan.php +++ b/modules/EmailMan/EmailMan.php @@ -1385,7 +1385,13 @@ private function sendOptInEmailViaMailer(SugarBean $focus, EmailAddress $emailAd isset($focus->first_name) ? $focus->first_name : ''); $mailer->replace('contact_last_name', isset($focus->last_name) ? $focus->last_name : ''); - $mailer->replace('emailaddress_email_address', $emailAddressString); + $emailAddressConfirmOptInToken = $emailAddress->getConfirmOptInTokenGenerateIfNotExists(); + $mailer->replace('emailaddress_confirm_opt_in_token', $emailAddressConfirmOptInToken); + + /** + * @deprecated since version 7.10.2 + */ + $mailer->replace('emailaddress_email_address', $emailAddressConfirmOptInToken); $mailer->replace('sugarurl', $sugar_config['site_url']); diff --git a/modules/Emails/language/en_us.lang.php b/modules/Emails/language/en_us.lang.php index 54dfde61d79..93721eb1f18 100644 --- a/modules/Emails/language/en_us.lang.php +++ b/modules/Emails/language/en_us.lang.php @@ -373,6 +373,7 @@ 'LBL_MARK_UNFLAGGED' => 'Mark As Unflagged', 'LBL_CONFIRM_OPT_IN_SENT_DATE' => 'Opt In Email Sent', 'LBL_CONFIRM_OPT_IN_FAIL_DATE' => 'Opt In Email Failed', + 'LBL_CONFIRM_OPT_IN_TOKEN' => 'Confirm Opt In Token', 'ERR_NO_RETURN_ID' => 'Attachment not found.',