Different opt-in templates per camaign

Hi,

I need a way to tell suite to use different templates for the opt-in based on a specific campaign, so that if somewhere stored with the campaign, the logic goes in another template, an not into “EntryPointConfirmOptIn.tpl”. How can this be done?

I never thought of this before, but from a quick look my first attempt would be to override this function to make it more complex to implement that requirement:

https://github.com/salesagility/SuiteCRM/blob/master/include/EntryPointConfirmOptInHandler.php#L183

I’m not sure if SuiteCRM will pick up a custom file for that, or if there is another place to override the class, you’ll have to investigate…

1 Like

OK, sounds like I will give that a try.

Sorry, I´m relativeley new to suit, so how can I override this function in an update-safe manner?

I am not sure there is an upgrade-safe way to do that. This is relatively new code, and it’s possible that nobody needed this level of customization before you, and maybe the mechanisms are not in place.

But a first attempt would be to create a copy in

custom/include/EntryPointConfirmOptInHandler.php

and see if it get’s picked up. If not, try the customization one level before, in the file that requires that one, by creating a copy of this other file

custom/include/entryPointConfirmOptInConnector.php

if that get’s picked up, then you can change the require of the handler to include your custom file.

If this doesn’t work then you either edit the core to make this customizable, or you go with a non upgrade-safe customization and make a note to be careful reinstating this after upgrades…

OK, sadly that failed.

Maybe i have to go deeper into it and override \include\MVC\Controller\entry_point_registry.php with an own new entry point to make this work?

Or just registering new entry points like in this solution:
https://suitecrm.com/suitecrm/forum/developer-help/15536-upgrade-safe-customization
?

OK,I added a new file called extOptIn.php into /custom/Extension/application/Ext/EntryPointRegistry/extOptIn.php with the following code:

<?php

$entry_point_registry = array(
    'extOptIn' => array('file' => 'include/entryPointConfirmOptInConnector.php', 'auth' => false),
);

But now all other entry points are not working anymore.

Any ideas?

OK, the code is wrong. Following the documentation it needs to be like this:

<?php
  $entry_point_registry['extOptIn'] = array(
      'file' => 'include/entryPointConfirmOptInConnector.php',
      'auth' => false,
  );

Then it works.

Yeah, that last one looks better :slight_smile:

I am glad you got it working, and thanks for sharing here the solution.

OK, some more problems.
I´ve copied EntryPointConfirmOptInHandler.php to custom/include/EntryPointConfirmOptInHandler.php
So far so good. Now i can modify the way the Opt in works and tweak the template.

BUT:
Based on the ‘confirm_opt_in_token’ I need more information from the user inside EntryPointConfirmOptInHandler.php . Like Firstname, Lastname, Campaign, and so on.
How can i access these information?

I don’t know, you’ll have to look :stuck_out_tongue:

Are you using an IDE (such as PhpStorm or Eclipse) where you can inspect the variables? You have $request which could have what you need in there, and if not, one level before (in the function that calls this one, callMethod) you have also $post variable which you can pass. Normally these variables bring in tons of useful things (which are going back and forth from the backend to the frontend).

Hi,

I use PHP storm, but the $request is not helping.

I am one step ahead, now with

$optInUser = BeanFactory::getBean('Contacts',current($people));

I can access the record of the user.

Now I can access

$optInUser->campaign_id

to get the ID of the campaign.

For campains I´ve added two custom fields, one is “campaign_optin_tpl”, where I store the opt-in template for the campaign.

So basically I know the campaign-ID and all the userdata, but how can I access the campaign fields to retrieve the value of “campaign_optin_tpl”?

That´s the only missing part, after that I will post my complete solution B-)

Campaigns are also beans, something like this should work

     $campaign = BeanFactory::getBean('Campaigns', $campaignId);
1 Like

OK, that worked. Hopefully that´s all I need to complete this.

Here is the complete “how to” to display different templates, based on the given campaign, as a result for the opt-in click.

First there are a few updates to the module campaign needed:
Inside studio add a new field called “campaign_optin_template_c” to the campaign module. This will hold the template information for the Opt-In response itself and overrides the default file “include/EntryPointConfirmOptIn.tpl”. You can place your templates in the following location:

custom/include/

Just keep in mind to name it correctly to match the content of the field “campaign_optin_template_c” inside your campaign.

Now on to the code additions which are needed.

  1. Create a new entry point that overrides the default mechanism for “ConfirmOptIn” in the following folder:
    custom/Extension/application/Ext/EntryPointRegistry/

I´ve called the file “extOptIn.php” . This overrides the original entry point “ConfirmOptIn”, which is located here: include/MVC/Controller/entry_point_registry.php

<?php
  $entry_point_registry['ConfirmOptIn'] = array(
      'file' => 'custom/include/entryPointOptInConnector.php',
      'auth' => false,
  );
  1. Create a custom connector for the override in the following location:
    custom/include/

I´ve called the file “entryPointOptInConnector.php”.

<?php

if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}

require_once __DIR__ . '/../include/EntryPointConfirmOptInHandler.php';
new EntryPointConfirmOptInHandler();
  1. Create a handler for the new connector inside the following location:
    custom/include/

I´ve called the file “EntryPointConfirmOptInHandler.php”

The code is based on the original code from “include/EntryPointConfirmOptInHandler.php”, but modified.

<?php

if (!defined('sugarEntry') || !sugarEntry) {
    die('Not A Valid Entry Point');
}

/**
 * Class EntryPointConfirmOptInHandler
 */
class EntryPointConfirmOptInHandler
{

    /**
     * @var EmailAddress $emailAddress
     */
    private $emailAddress;

    /**
     *
     * @param array $request
     * @param array $post
     */
    public function __construct($request = null, $post = null)
    {
        if (is_null($request)) {
            $request = $_REQUEST;
        }

        if (is_null($post)) {
            $post = $_POST;
        }

        $method = isset($request['method']) && $request['method'] ? $request['method'] : null;

        $output = $this->callMethod($method, $post, $request);

        echo $output;
        sugar_cleanup();
    }

    /**
     *
     * @param string $method
     * @param array $post
     * @param array $request
     * @return string
     */
    protected function callMethod($method, $post, $request)
    {
        switch ($method) {
            case 'confirmOptInSelected':
                $output = $this->methodConfirmOptInSelected($post);
                break;
            default:
                $output = $this->methodConfirmOptInUser($request);
                break;
        }
        return $output;
    }

    /**
     * @global array $app_strings
     * @param array $post
     * @return string|boolean
     */
    private function methodConfirmOptInSelected($post)
    {
        global $app_strings;

        $configurator = new Configurator();
        if (!$configurator->isConfirmOptInEnabled()) {
            return false;
        }

        $module = $post['module'];
        $uids = explode(',', $post['uid']);
        $confirmedOptInEmailsSent = 0;
        $errors = 0;
        $warnings = 0;
        $msg = '';

        foreach ($uids as $uid) {
            $emailMan = new EmailMan();
            if (!$emailMan->addOptInEmailToEmailQueue($module, $uid)) {
                $errors++;
            } elseif ($emailMan->getLastOptInWarn()) {
                $warnings++;
            } else {
                $confirmedOptInEmailsSent++;
            }
        }

        if ($confirmedOptInEmailsSent > 0) {
            $msg .= sprintf($app_strings['RESPONSE_SEND_CONFIRM_OPT_IN_EMAIL'], $confirmedOptInEmailsSent);
        }

        if ($warnings > 0) {
            $msg .=  sprintf($app_strings['RESPONSE_SEND_CONFIRM_OPT_IN_EMAIL_NOT_OPT_IN'], $warnings);
        }

        if ($errors > 0) {
            $msg .=  sprintf($app_strings['RESPONSE_SEND_CONFIRM_OPT_IN_EMAIL_MISSING_EMAIL_ADDRESS_ID'], $errors);
        }


        return $msg;
    }

    /**
     * Confirm Opt In User
     *
     * @param array $request
     * @return string
     */
    private function methodConfirmOptInUser($request)
    {

        $emailAddress = BeanFactory::getBean('EmailAddresses');
        $this->emailAddress = $emailAddress->retrieve_by_string_fields([
            'confirm_opt_in_token' => $request['from']
        ]);

         if ($this->emailAddress) {
            $this->emailAddress->confirmOptIn();
            $this->emailAddress->save();

            $people = $this->getIDs($this->emailAddress->email_address, 'Contacts');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people, 'Contacts');
                $personid=$people;
            }
            $people = $this->getIDs($this->emailAddress->email_address, 'Leads');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people, 'Leads');
                $personid=$people;
            }

            $people = $this->getIDs($this->emailAddress->email_address, 'Prospects');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people,  'Prospects');
                $personid=$people;
            }
        }
        $template = new Sugar_Smarty();
        $template->assign('FOCUS', $this->emailAddress);

        $optInUser = BeanFactory::getBean('Contacts',current($personid));
        $targetCampaign = BeanFactory::getBean('Campaigns', $optInUser->campaign_id);

        if ( empty ( $targetCampaign->campaign_optin_template_c ) ){
            return $template->fetch('include/EntryPointConfirmOptIn.tpl');
        }else{
            return $template->fetch($targetCampaign->campaign_optin_template_c);
        }
    }

    /**
     * @param String $email
     * @param String $module
     *
     * @return array|bool
     */
    private function getIDs($email, $module) {
        $people = $this->emailAddress->getRelatedId($email, $module);
        return $people;
    }

    /**
     * @param array $people
     */
    private function setLawfulBasisForEachPerson(array $people, $module) {
        /** @var Person $person */
        foreach ($people as $person) {
            $bean = BeanFactory::getBean($module, $person);
            if($bean) {
                if(!$bean->setLawfulBasis('consent', 'email')){
                    LoggerManager::getLogger()->warn('Lawful basis saving failed for record ' . $bean->name);
                }
            }
        }
    }
}

The magic itself happens in the following part of the code, which needs to be modified:

private function methodConfirmOptInUser($request)
    {

        $emailAddress = BeanFactory::getBean('EmailAddresses');
        $this->emailAddress = $emailAddress->retrieve_by_string_fields([
            'confirm_opt_in_token' => $request['from']
        ]);

         if ($this->emailAddress) {
            $this->emailAddress->confirmOptIn();
            $this->emailAddress->save();

            $people = $this->getIDs($this->emailAddress->email_address, 'Contacts');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people, 'Contacts');
                $personid=$people;
            }
            $people = $this->getIDs($this->emailAddress->email_address, 'Leads');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people, 'Leads');
                $personid=$people;
            }

            $people = $this->getIDs($this->emailAddress->email_address, 'Prospects');
            if ($people) {
                $this->setLawfulBasisForEachPerson($people,  'Prospects');
                $personid=$people;
            }
        }
        $template = new Sugar_Smarty();
        $template->assign('FOCUS', $this->emailAddress);

        $optInUser = BeanFactory::getBean('Contacts',current($personid));
        $targetCampaign = BeanFactory::getBean('Campaigns', $optInUser->campaign_id);

        if ( empty ( $targetCampaign->campaign_optin_template_c ) ){
            return $template->fetch('include/EntryPointConfirmOptIn.tpl');
        }else{
            return $template->fetch($targetCampaign->campaign_optin_template_c);
        }
    }

This function now fetches the correct template given by the confirm_opt_in_token. That´s it.

1 Like

Thanks for sharing this, DirkDV!