Different Sender E-Mail Adresses for Workflow E-Mails

Hello,

i am actually struggeling with the problem that my clients respond always to the one system e-mail adress cause we are sending a lot of emails via workflows. The Sender E-Mail Adress is always the system email address.

Is there any way to change the Sender Mail Adress individuall per Workflow if we use the “Send E-Mail” Action? This would help me a lot.
And is there an other way to send workflow emails from assigned users email adresses?

If both will be possible that would help me really a lot.
Thanks a lot.

I have not tried this, But the logic to this seem correct. This has worked for custom scripts i have wrote sending emails.

So the work flow, make sure it is assigned to the user you want to send it by:

under admin - > users -> select user -> edit -> Email settings - Setting button

then mail account
image

add the users email details

another option is on the mail server, give users permsion to send on behalf of your system mail account and then tick this box in admin -> email settings:

OK, thank you a lot.

Do i understand this correct: I only need to change the assigned user and maybe need to allow to send notifications from the user adress?

So no coding is necessary?

Thank you very much for this tipp, if this is working.

Hey,

i tested the Logic you told me.
It is not working as you told, Sender is always the System Mail Adress.

Do you have any other ideas?

Hey,
Im also interested on knowing the solution. Has there been any follow up on this?
I also tried the same thing and didnt work for me.

I don’t believe this is possible out of the box. I know with @pgr plugin, he has some features to modify who emails come from.

I will share with you a code based solution that allowed me to achieve what I want without using paid plugins.

You will have to allow the params from the workflow to also allow to add a “from address” and then change the send email action to be able to work with that parameter. However, for that to work you need to edit the function edit_display to allow to introduce a from address in the form. I will share with you what I did

First you need to change the /include/language/es_es.lang.php and add the following line

$app_list_strings[‘aow_email_to_list_from’][‘from’] = ‘From’;

Then you need to change the javascript file that interacts with the responsive part of the edit_display function add_emailLine in the file AOW_Actions/actions/actionSendEmail.js so it allows the field from to be placed in the aow_actions parameters

function add_emailLine(ln){

    var aow_email_type_list = document.getElementById("aow_email_type_list").value;
    var aow_email_to_list = document.getElementById("aow_email_to_list").value;

    if (emailln[ln] == null) {
        emailln[ln] = 1; // Start from 1 to avoid conflicting with the single "from" entry
    }

    tablebody = document.createElement("tbody");
    tablebody.id = 'emailLine'+ln+'_body' + emailln[ln];
    document.getElementById('emailLine'+ln+'_table').appendChild(tablebody);

    var x = tablebody.insertRow(-1);
    x.id = 'emailLine'+ln+'_line' + emailln[ln];

    var a = x.insertCell(0);
    a.innerHTML = "<button type='button' id='emailLine"+ln+"_delete" + emailln[ln]+"' class='button' value='Remove Line' tabindex='116' onclick='clear_emailLine(" + ln + ",this);'><span class='suitepicon suitepicon-action-minus'></span></button> ";

    // Remove the "from" option from other dropdowns
    aow_email_to_list = aow_email_to_list.replace(/<option value='from'>.*?<\/option>/, '');

    a.innerHTML += "<select tabindex='116' name='aow_actions_param["+ln+"][email_to_type]["+emailln[ln]+"]' id='aow_actions_param"+ln+"_email_to_type"+emailln[ln]+"'>" + aow_email_to_list + "</select> ";
    a.innerHTML += "<select tabindex='116' name='aow_actions_param["+ln+"][email_target_type]["+emailln[ln]+"]' id='aow_actions_param"+ln+"_email_target_type"+emailln[ln]+"' onchange='show_emailField(" + ln + "," + emailln[ln] + ");'>" + aow_email_type_list + "</select> ";
    a.innerHTML += "<span id='emailLine"+ln+"_field"+emailln[ln]+"'><input id='aow_actions_param["+ln+"][email]["+emailln[ln]+"]' type='text' tabindex='116' size='25' name='aow_actions_param["+ln+"][email]["+emailln[ln]+"]'></span>";

    emailln[ln]++;

    return emailln[ln] - 1;
}

Next, you will need to change the following functions in the file AOW_Actions/actions/actionSendEmail.php

edit_display

    public function edit_display($line, SugarBean $bean = null, $params = array())
    {
        global $app_list_strings;
        $email_templates_arr = get_bean_select_array(true, 'EmailTemplate', 'name', '', 'name');

        if (!in_array($bean->module_dir, getEmailableModules())) {
            unset($app_list_strings['aow_email_type_list']['Record Email']);
        }
        $targetOptions = getRelatedEmailableFields($bean->module_dir);
        if (empty($targetOptions)) {
            unset($app_list_strings['aow_email_type_list']['Related Field']);
        }

        // Prepare options without 'from' in the list
        $aow_email_to_list = $app_list_strings['aow_email_to_list'];
        unset($aow_email_to_list['from']);
        $to_list_options = get_select_options_with_id($aow_email_to_list, '');

        // Prepare hidden fields for select options
        $html = '<input type="hidden" name="aow_email_type_list" id="aow_email_type_list" value="' . get_select_options_with_id($app_list_strings['aow_email_type_list'], '') . '">
                 <input type="hidden" name="aow_email_to_list" id="aow_email_to_list" value="' . $to_list_options . '">';

        $checked = isset($params['individual_email']) && $params['individual_email'] ? 'CHECKED' : '';

        // Start the HTML structure for the Form elements
        $html .= "<table border='0' cellpadding='0' cellspacing='0' width='100%' data-workflow-action='send-email'>";
        $html .= "<tr>";
        $html .= '<td valign="top" scope="row"><label>' . translate("LBL_INDIVIDUAL_EMAILS", "AOW_Actions") . ':</label></td>';
        $html .= "<td valign='top'>";
        $html .= "<input type='hidden' name='aow_actions_param[".$line."][individual_email]' value='0' >";
        $html .= "<input type='checkbox' id='aow_actions_param[".$line."][individual_email]' name='aow_actions_param[".$line."][individual_email]' value='1' $checked></td>";
        $html .= '</td>';

        $params['email_template'] = isset($params['email_template']) ? $params['email_template'] : '';
        $hidden = $params['email_template'] != '' ? "" : "style='visibility: hidden;'";

        $html .= '<td id="name_label" scope="row" valign="top"><label>' . translate("LBL_EMAIL_TEMPLATE", "AOW_Actions") . ':<span class="required">*</span></label></td>';
        $html .= "<td valign='top'>";
        $html .= "<select name='aow_actions_param[".$line."][email_template]' id='aow_actions_param_email_template".$line."' onchange='show_edit_template_link(this," . $line . ");'>" . get_select_options_with_id($email_templates_arr, $params['email_template'])."</select>";
        $html .= "&nbsp;<a href='javascript:open_email_template_form(".$line.")' >".translate('LBL_CREATE_EMAIL_TEMPLATE', 'AOW_Actions')."</a>";
        $html .= "&nbsp;<span name='edit_template' id='aow_actions_edit_template_link".$line."' $hidden><a href='javascript:edit_email_template_form(".$line.")' >".translate('LBL_EDIT_EMAIL_TEMPLATE', 'AOW_Actions')."</a></span>";
        $html .= "</td>";
        $html .= "</tr>";
        // Single line email input for "From" address using separate translation and numeric index
        $html .= "<tr>";
        $html .= "<td id='from_label' scope='row' valign='top'><label>Email:</label></td>";
        $html .= "<td colspan='3'>";
        $html .= "<table id='singleEmailLine" . $line . "_table' width='100%' class='email-line' style='margin-bottom: 10px;'>";
        $html .= "<tr><td>";
        $html .= "<select tabindex='116' name='aow_actions_param[" . $line . "][email_to_type][0]' id='aow_actions_param" . $line . "_email_to_type0'>";
        $html .= "<option value='from'>From</option>";
        $html .= "</select>";
        $html .= "<select tabindex='116' name='aow_actions_param[" . $line . "][email_target_type][0]' id='aow_actions_param" . $line . "_email_target_type0' onchange='show_emailField(" . $line . ", 0);'>";
        $html .= get_select_options_with_id($app_list_strings['aow_email_type_list'], 'Email Address');
        $html .= "</select>";
        $html .= "<span id='emailLine" . $line . "_field0'><input id='aow_actions_param[" . $line . "][email][0]' type='text' tabindex='116' size='25' name='aow_actions_param[" . $line . "][email][0]' value='" . (isset($params['email'][0]) ? $params['email'][0] : '') . "'></span>";
        $html .= "</td></tr>";
        $html .= "</table>";
        $html .= "</td>";
        $html .= "</tr>";

        // "To", "CC", "BCC" fields table
        $html .= "<tr>";
        $html .= '<td id="to_label" scope="row" valign="top" style="display:none;"><label>Para:</label></td>';
        $html .= '<td colspan="3">';
        $html .= '<button type="button" onclick="add_emailLine(' . $line . ')" id="plus-icon"><span class="suitepicon suitepicon-action-plus"></span></button>';
        $html .= '<table id="emailLine' . $line . '_table" width="100%" class="email-line">';

        // If there are no existing lines, add a default one
        if (!isset($params['email_target_type']) || empty($params['email_target_type'])) {
            $html .= '<tbody id="emailLine'.$line.'_body0">';
            $html .= '<tr id="emailLine'.$line.'_line0">';
            $html .= '<td><button type="button" id="emailLine'.$line.'_delete0" class="button" value="Remove Line" tabindex="116" onclick="clear_emailLine(' . $line . ', this);"><span class="suitepicon suitepicon-action-minus"></span></button> ';
            $html .= '<select tabindex="116" name="aow_actions_param['.$line.'][email_to_type][1]" id="aow_actions_param'.$line.'_email_to_type1">' . $to_list_options . '</select> ';
            $html .= '<select tabindex="116" name="aow_actions_param['.$line.'][email_target_type][1]" id="aow_actions_param'.$line.'_email_target_type1" onchange="show_emailField(' . $line . ',1);">' . get_select_options_with_id($app_list_strings['aow_email_type_list'], '') . '</select> ';
            $html .= '<span id="emailLine'.$line.'_field1"><input id="aow_actions_param['.$line.'][email][1]" type="text" tabindex="116" size="25" name="aow_actions_param['.$line.'][email][1]" value=""></span></td>';
            $html .= '</tr>';
            $html .= '</tbody>';
        }

        $html .= '</table>';
        $html .= '</td>';
        $html .= "</tr>";
        $html .= "</table>";

        // JavaScript initialization
        $html .= "<script id ='aow_script".$line."'>";
        if (isset($params['email_target_type'])) {
            foreach ($params['email_target_type'] as $key => $field) {
                // Ignore lines where email_to_type is "from"
                if ($params['email_to_type'][$key] === 'from') {
                    continue;
                }
                if (is_array($params['email'][$key])) {
                    $params['email'][$key] = json_encode($params['email'][$key]);
                }
                $html .= "load_emailline('".$line."','".$params['email_to_type'][$key]."','".$params['email_target_type'][$key]."','".$params['email'][$key]."');";
            }
        }
        $html .= "</script>";

        return $html;
    }

Then the getEmailsFromParams function

    protected function getEmailsFromParams(SugarBean $bean, $params)
    {
        $emails = array();
        $emailFrom = null;
        //backward compatible
        if (isset($params['email_target_type']) && !is_array($params['email_target_type'])) {
            $email = '';
            switch ($params['email_target_type']) {
                case 'Email Address':
                    $params['email'] = array($params['email']);
                    break;
                case 'Specify User':
                    $params['email'] = array($params['email_user_id']);
                    break;
                case 'Related Field':
                    $params['email'] = array($params['email_target']);
                    break;
            }
            $params['email_target_type'] = array($params['email_target_type']);
            $params['email_to_type'] = array('to');
        }
        //end backward compatible
        if (isset($params['email_target_type'])) {
            foreach ($params['email_target_type'] as $key => $field) {
                switch ($field) {
                    case 'Email Address':
                        if (trim($params['email'][$key]) != '') {
                            if ($params['email_to_type'][$key] === 'from') {
                                $emailFrom = $params['email'][$key];  // Set the 'from' email address
                            } else {
                                $emails[$params['email_to_type'][$key]][] = $params['email'][$key];
                            }
                        }
                        break;
                    case 'Specify User':
                        $user = BeanFactory::newBean('Users');
                        $user->retrieve($params['email'][$key]);
                        $user_email = $user->emailAddress->getPrimaryAddress($user);
                        if (trim($user_email) != '') {
                            if ($params['email_to_type'][$key] === 'from') {
                                $emailFrom = $user_email;  // Set the 'from' email address
                            } else {
                                $emails[$params['email_to_type'][$key]][] = $user_email;
                                $emails['template_override'][$user_email] = array('Users' => $user->id);
                            }
                        }
                        break;
                    case 'Users':
                        $users = array();
                        switch ($params['email'][$key][0]) {
                            case 'security_group':
                                if (file_exists('modules/SecurityGroups/SecurityGroup.php')) {
                                    require_once('modules/SecurityGroups/SecurityGroup.php');
                                    $security_group = BeanFactory::newBean('SecurityGroups');
                                    $security_group->retrieve($params['email'][$key][1]);
                                    $users = $security_group->get_linked_beans('users', 'User');
                                    $r_users = array();
                                    if ($params['email'][$key][2] != '') {
                                        require_once('modules/ACLRoles/ACLRole.php');
                                        $role = BeanFactory::newBean('ACLRoles');
                                        $role->retrieve($params['email'][$key][2]);
                                        $role_users = $role->get_linked_beans('users', 'User');
                                        foreach ($role_users as $role_user) {
                                            $r_users[$role_user->id] = $role_user->name;
                                        }
                                    }
                                    foreach ($users as $user_id => $user) {
                                        if ($params['email'][$key][2] != '' && !isset($r_users[$user->id])) {
                                            unset($users[$user_id]);
                                        }
                                    }
                                    break;
                                }
                            //No Security Group module found - fall through.
                            // no break
                            case 'role':
                                require_once('modules/ACLRoles/ACLRole.php');
                                $role = BeanFactory::newBean('ACLRoles');
                                $role->retrieve($params['email'][$key][2]);
                                $users = $role->get_linked_beans('users', 'User');
                                break;
                            case 'all':
                            default:
                                $db = DBManagerFactory::getInstance();
                                $sql = "SELECT id from users WHERE status='Active' AND portal_only=0 ";
                                $result = $db->query($sql);
                                while ($row = $db->fetchByAssoc($result)) {
                                    $user = BeanFactory::newBean('Users');
                                    $user->retrieve($row['id']);
                                    $users[$user->id] = $user;
                                }
                                break;
                        }
                        foreach ($users as $user) {
                            $user_email = $user->emailAddress->getPrimaryAddress($user);
                            if (trim($user_email) != '') {
                                $emails[$params['email_to_type'][$key]][] = $user_email;
                                $emails['template_override'][$user_email] = array('Users' => $user->id);
                            }
                        }
                        break;
                    case 'Related Field':
                        $emailTarget = $params['email'][$key];
                        $relatedFields = array_merge($bean->get_related_fields(), $bean->get_linked_fields());
                        $field = $relatedFields[$emailTarget];
                        if ($field['type'] == 'relate') {
                            $linkedBeans = array();
                            $idName = $field['id_name'];
                            $id = $bean->$idName;
                            if(gettype($id)=='string') {
                                $linkedBeans[] = BeanFactory::getBean($field['module'], $id);
                            } else {
                                $id = reset($id->get());
                                $linkedBeans[] = BeanFactory::getBean($field['module'], $id);
                            }
                        } else {
                            if ($field['type'] == 'link') {
                                $relField = $field['name'];
                                if (isset($field['module']) && $field['module'] != '') {
                                    $rel_module = $field['module'];
                                } else {
                                    if ($bean->load_relationship($relField)) {
                                        $rel_module = $bean->$relField->getRelatedModuleName();
                                    }
                                }
                                $linkedBeans = $bean->get_linked_beans($relField, $rel_module);
                            } else {
                                $linkedBeans = $bean->get_linked_beans($field['link'], $field['module']);
                            }
                        }
                        if ($linkedBeans) {
                            foreach ($linkedBeans as $linkedBean) {
                                if (!empty($linkedBean)) {
                                    $rel_email = $linkedBean->emailAddress->getPrimaryAddress($linkedBean);
                                    if (trim($rel_email) != '') {
                                        $emails[$params['email_to_type'][$key]][] = $rel_email;
                                        $emails['template_override'][$rel_email] = array($linkedBean->module_dir => $linkedBean->id);
                                    }
                                }
                            }
                        }
                        break;
                    case 'Record Email':
                        $recordEmail = $bean->emailAddress->getPrimaryAddress($bean);
                        if ($recordEmail == '' && isset($bean->email1)) {
                            $recordEmail = $bean->email1;
                        }
                        if (trim($recordEmail) != '') {
                            $emails[$params['email_to_type'][$key]][] = $recordEmail;
                        }
                        break;
                }
            }
        }
        $emails['from'] = $emailFrom;  // Add the 'from' email address to the array
        return $emails;
    }

Finally you will add two custom functions and edit the sendEmail so you can send emails through outboundemails defined in the system

    protected function getUserMailerSettingsByFromAddress($emailFrom)
    {
        $db = DBManagerFactory::getInstance();

        // Query the outbound_email table to fetch the settings where mail_smtpuser matches the emailFrom
        $query = "
            SELECT id
            FROM outbound_email
            WHERE mail_smtpuser = '" . $db->quote($emailFrom) . "' 
            AND deleted = 0 
            LIMIT 1";

        $result = $db->query($query);
        $outbound = $db->fetchByAssoc($result);

        // If outbound email settings are found, retrieve the OutboundEmailAccounts object
        if ($outbound) {
            $outboundEmailAccount = BeanFactory::getBean('OutboundEmailAccounts', $outbound['id']);
            return $outboundEmailAccount;
        }

        // If no specific settings found, fallback to system mailer settings
        $GLOBALS['log']->fatal("No specific outbound email settings found for: " . $emailFrom);
        $oe = new OutboundEmail();
        return $oe->getSystemMailerSettings();
    }


    protected function setupMailerFromOutbound(OutboundEmailAccounts $oe, SugarPHPMailer $mail): void
    {
        // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
        $mail->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
        if (isSmtp($oe->mail_sendtype ?? '')) {
            //Set mail send type information
            $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;
            }
        } else {
            $mail->Mailer = "sendmail";
        }
    }

    public function sendEmail($emailTo, $emailSubject, $emailBody, $altemailBody, SugarBean $relatedBean = null, $emailCc = array(), $emailBcc = array(), $attachments = array(), $emailFrom = null)
    {
        require_once('modules/Emails/Email.php');
        require_once('include/SugarPHPMailer.php');
        require_once('include/OutboundEmail/OutboundEmail.php');  // To fetch credentials

        $emailObj = BeanFactory::newBean('Emails');
        $defaults = $emailObj->getSystemDefaultEmail();
        $mail = new SugarPHPMailer();
        $mail->setMailerForSystem();

        // $GLOBALS['log']->fatal("Default system email: " . print_r($defaults['email'], true));
        // $GLOBALS['log']->fatal("Provided emailFrom: " . print_r($emailFrom, true));

        // Use the provided 'from' address if available; otherwise, use the system default
        $mail->From = $emailFrom ? $emailFrom : $defaults['email'];
        isValidEmailAddress($mail->From);  // Ensure the 'from' email is valid
        $mail->FromName = $emailFrom ? $emailFrom : $defaults['name'];

        // Check if the email is from the seepmode.com domain (Default System Domain)
        if (strpos($emailFrom, '@seepmode.com') !== false) {
            // Use the system mail settings for seepmode.com
            $GLOBALS['log']->fatal("Using system email settings for: " . $defaults['email']);
        } else {
            // Fetch the outbound email settings based on the emailFrom address
            $outbound = $this->getUserMailerSettingsByFromAddress($emailFrom);

            // Handle the case where no settings are found
            if (!$outbound || !$outbound instanceof OutboundEmailAccounts) {
                $GLOBALS['log']->fatal("No valid SMTP settings found for: " . $emailFrom);
                return false;  // Fail if no SMTP settings are found
            }

            // Set up the mailer using the extracted settings
            $this->setupMailerFromOutbound($outbound, $mail);
            // $GLOBALS['log']->fatal("Using SMTP credentials for: " . $emailFrom);
        }

        // $GLOBALS['log']->fatal("Final From email used: " . print_r($mail->From, true));

        $mail->ClearAllRecipients();
        $mail->ClearReplyTos();
        $mail->Subject = from_html($emailSubject);
        $mail->Body = $emailBody;
        $mail->AltBody = $altemailBody;
        $mail->handleAttachments($attachments);
        $mail->prepForOutbound();

        if (empty($emailTo) || !is_array($emailTo)) {
            $GLOBALS['log']->fatal("Email sending failed: No valid recipients found.");
            return false;
        }
        foreach ($emailTo as $to) {
            $mail->AddAddress($to);
        }
        if (!empty($emailCc)) {
            foreach ($emailCc as $email) {
                $mail->AddCC($email);
            }
        }
        if (!empty($emailBcc)) {
            foreach ($emailBcc as $email) {
                $mail->AddBCC($email);
            }
        }
        if (!is_array($emailCc)) {
            $emailCc = [];
        }

        if (!is_array($emailBcc)) {
            $emailBcc = [];
        }

        //now create email
        if ($mail->Send()) {
            // $GLOBALS['log']->fatal("Email sent successfully to: " . implode(',', $emailTo));
            $emailObj->to_addrs = implode(',', $emailTo);
            $emailObj->cc_addrs = implode(',', $emailCc);
            $emailObj->bcc_addrs = implode(',', $emailBcc);
            $emailObj->type = 'out';
            $emailObj->deleted = '0';
            $emailObj->name = $mail->Subject;
            $emailObj->description = $mail->AltBody;
            $emailObj->description_html = $mail->Body;
            $emailObj->from_addr = $mail->From;
            isValidEmailAddress($emailObj->from_addr);
            if ($relatedBean instanceof SugarBean && !empty($relatedBean->id)) {
                $emailObj->parent_type = $relatedBean->module_dir;
                $emailObj->parent_id = $relatedBean->id;
            }
            $emailObj->date_sent_received = TimeDate::getInstance()->nowDb();
            $emailObj->modified_user_id = '1';
            $emailObj->created_by = '1';
            $emailObj->status = 'sent';
            $emailObj->save();

            // Fix for issue 1561 - Email Attachments Sent By Workflow Do Not Show In Related Activity.
            foreach ($attachments as $attachment) {
                $note = BeanFactory::newBean('Notes');
                $note->id = create_guid();
                $note->date_entered = $attachment->date_entered;
                $note->date_modified = $attachment->date_modified;
                $note->modified_user_id = $attachment->modified_user_id;
                $note->assigned_user_id = $attachment->assigned_user_id;
                $note->new_with_id = true;
                $note->parent_id = $emailObj->id;
                $note->parent_type = $attachment->parent_type;
                $note->name = $attachment->name;
                ;
                $note->filename = $attachment->filename;
                $note->file_mime_type = $attachment->file_mime_type;
                $fileLocation = "upload://{$attachment->id}";
                $dest = "upload://{$note->id}";
                if (!copy($fileLocation, $dest)) {
                    $GLOBALS['log']->debug("EMAIL 2.0: could not copy attachment file to $fileLocation => $dest");
                }
                $note->save();
            }
            return true;
        } else {
            // Log the error from PHPMailer
            $GLOBALS['log']->fatal("Email sending failed. Error: " . $mail->ErrorInfo);
        }
        return false;
    }
``
2 Likes

My plugins are coming to core SuiteCRM V8 very soon.

And I am also considering options to make them work in V7. The difficulties are mostly technical but I hope they can be overcome.

1 Like