How to set tracking URL for custom link?

Lakjin

Active Member
I've added custom markup to at function getCommonTagsSearchReplace in CampaignHelper.php; the markup involves inserting URLs into campaigns. The markup itself works fine. However, the URLs are not being parsed into tracking URLs. Any way for me to do that?

Thanks!
 

twisted1919

Administrator
Staff member
I am away from the code now, but i think the app trats tags that end with _URL] specially, so try to name your custom tags that are transformed into urls like [WHATEVER_DO_THIS_URL]
 

Lakjin

Active Member
I am away from the code now, but i think the app trats tags that end with _URL] specially, so try to name your custom tags that are transformed into urls like [WHATEVER_DO_THIS_URL]
Tried that, won't work :-( I think the function for parsing URLs into tracking URLs is called before getCommonTagsSearchReplace, so my markup doesn't have the URLs in there yet.
 

twisted1919

Administrator
Staff member
that would make sense and you'd have to hook in the behavior file directly, which isn't that desirable.
 

twisted1919

Administrator
Staff member
There's no other way... Maybe we can add a new filter hook inthere and you could then use it?
But you'll have to tell me what exactly you need and where you want the changes to happen in code so that i can take proper decision for the hook.
 

Lakjin

Active Member
There's no other way... Maybe we can add a new filter hook inthere and you could then use it?
But you'll have to tell me what exactly you need and where you want the changes to happen in code so that i can take proper decision for the hook.
What I needed was the ability to show different content to each user. So I added a shortcode (e.g. [MYSHORTCODE]) to my campaign and in getCommonTagsSearchReplace I replace that shortcode with my code to generate content for each user. This way, when a campaign is processed for each user, everyone gets the different content I want to show them as opposed to everyone getting the same thing.

As part of this new markup, I have links that I would prefer to be tracked by MailWizz.
 

twisted1919

Administrator
Staff member
@Lakjin - Current code says:
PHP:
if (!$onlyPlainText) {
            if (($emailFooter = $customer->getGroupOption('campaigns.email_footer')) && strlen(trim($emailFooter)) > 5) {
                $emailContent = CampaignHelper::injectEmailFooter($emailContent, $emailFooter, $campaign);
            }
           
            if (!empty($campaign->option) && !empty($campaign->option->embed_images) && $campaign->option->embed_images == CampaignOption::TEXT_YES) {
                list($emailContent, $embedImages) = CampaignHelper::embedContentImages($emailContent, $campaign);
            }
           
            if (!empty($campaign->option) && $campaign->option->xml_feed == CampaignOption::TEXT_YES) {
                $emailContent = CampaignXmlFeedParser::parseContent($emailContent, $campaign, $subscriber, true);
            }
           
            if (!empty($campaign->option) && $campaign->option->json_feed == CampaignOption::TEXT_YES) {
                $emailContent = CampaignJsonFeedParser::parseContent($emailContent, $campaign, $subscriber, true);
            }
   
            if (!empty($campaign->option) && $campaign->option->url_tracking == CampaignOption::TEXT_YES) {
                $emailContent = CampaignHelper::transformLinksForTracking($emailContent, $campaign, $subscriber, true);
            }
           
            $emailData = CampaignHelper::parseContent($emailContent, $campaign, $subscriber, true);    
            list($toName, $emailSubject, $emailContent) = $emailData;
        }
The parts that we are interested in is
PHP:
            if (!empty($campaign->option) && $campaign->option->url_tracking == CampaignOption::TEXT_YES) {
                $emailContent = CampaignHelper::transformLinksForTracking($emailContent, $campaign, $subscriber, true);
            }
What we can do is to have a hook that executes like:
PHP:
           if (!empty($campaign->option) && $campaign->option->url_tracking == CampaignOption::TEXT_YES) {
                $emailContent = Yii::app()->hooks->applyFilters('some_hook_name_before_transform_links_for_tracking', $emailContent, $campaign, $subscriber, true);
                $emailContent = CampaignHelper::transformLinksForTracking($emailContent, $campaign, $subscriber, true);
                $emailContent = Yii::app()->hooks->applyFilters('some_hook_name_after_transform_links_for_tracking', $emailContent, $campaign, $subscriber, true);
            }
and then you could eventually do, from an extension for example:
PHP:
Yii::app()->hooks->addFilter('some_hook_name_before_transform_links_for_tracking', function($emailContent, $campaign, $subscriber, $bool){
     // alter the email content, i.e:
     $emailContent = str_replace($arrayOfTags, $arrayOfValues, $emailContent);
     return $emailContent;
});
If that helps you i could implement and then give you modified files.
 

Lakjin

Active Member
If I were to throw my custom code in here:

Code:
Yii::app()->hooks->addFilter('some_hook_name_before_transform_links_for_tracking', function($emailContent, $campaign, $subscriber, $bool){
    //My code here
     return $emailContent;
});
Would this coded be executed on a per subscriber basis for each campaign or once per campaign? I ask because I need the shortcode replaced for each user individually (e.g. just like how [UPDATE_PROFILE] is a unique link for each user) as opposed to replace the shortcode once per campaign.
 

twisted1919

Administrator
Staff member
It's for each subscriber ;)
Basically $subscriber is always a different one.

I went further and implemented this:
Open apps/common/components/helpers/CampaignHelper.php and find the transformLinksForTracking() public static method and make it:

PHP:
public static function transformLinksForTracking($content, Campaign $campaign, ListSubscriber $subscriber, $canSave = false)
    {
        $content    = StringHelper::decodeSurroundingTags($content);
        $list       = $campaign->list;
        $mustSave   = false;
       
        
        $content = Yii::app()->hooks->applyFilters('campaign_content_before_transform_links_for_tracking', $content, $campaign, $subscriber, $list);
       
        if (!isset(self::$_trackingUrls[$campaign->campaign_id])) {
           
            self::$_trackingUrls[$campaign->campaign_id] = array();
           
            $urlModelsCount = 0;
            if ($canSave) {
                $urlModelsCount = CampaignUrl::model()->countByAttributes(array(
                    'campaign_id' => $campaign->campaign_id,
                ));    
            }

            $mustSave       = $urlModelsCount == 0;
            $baseUrl        = Yii::app()->options->get('system.urls.frontend_absolute_url');
            $trackingUrl    = $baseUrl . 'campaigns/[CAMPAIGN_UID]/track-url/[SUBSCRIBER_UID]';
   
            // (\042|\047) are octal quotes.
            $pattern = '/href(\s+)?=(\s+)?(\042|\047)(\s+)?(.*?)(\s+)?(\042|\047)/i';
            if (!preg_match_all($pattern, $content, $matches)) {
                return $content;
            }
           
            $urls = $matches[5];
            $urls = array_map('trim', $urls);
            // combine url with markup
            $urls = array_combine($urls, $matches[0]);
            $foundUrls = array();
           
            foreach ($urls as $url => $markup) {
   
                // external url which may contain one or more tags(sharing maybe?)
                if (preg_match('/https?.*/i', $url, $matches) && filter_var($url, FILTER_VALIDATE_URL)) {
                    $_url = trim($matches[0]);
                    $foundUrls[$_url] = $markup;
                    continue;
                }
               
                // local tag to be transformed
                if (preg_match('/^\[([A-Z_]+)_URL\]$/', $url, $matches)) {
                    $_url = trim($matches[0]);
                    $foundUrls[$_url] = $markup;
                    continue;
                }
            }
           
            if (empty($foundUrls)) {
            
                $content = Yii::app()->hooks->applyFilters('campaign_content_after_transform_links_for_tracking', $content, $campaign, $subscriber, $list);
                return $content;
            }
   
            $prefix = $campaign->campaign_uid;
            $sort   = array();
           
            foreach ($foundUrls as $url => $markup) {

                $urlHash = sha1($prefix . $url);
                $track   = $trackingUrl . '/' . $urlHash;
                $length  = strlen($url);
               
                self::$_trackingUrls[$campaign->campaign_id][] = array(
                    'url'       => $url,
                    'hash'      => $urlHash,
                    'track'     => $track,
                    'length'    => $length,
                    'markup'    => $markup,
                );
               
                $sort[] = $length;
            }
           
            unset($foundUrls);    
            // make sure we order by the longest url to the shortest
            array_multisort($sort, SORT_DESC, SORT_NUMERIC, self::$_trackingUrls[$campaign->campaign_id]);
           
            // one final check
            if (!$mustSave) {
                $mustSave = count(self::$_trackingUrls[$campaign->campaign_id]) != $urlModelsCount;
            }
        }
       
        if (!empty(self::$_trackingUrls[$campaign->campaign_id])) {
           
            $searchReplace = array();
            foreach (self::$_trackingUrls[$campaign->campaign_id] as $urlData) {
                $searchReplace[$urlData['markup']] = 'href="'.$urlData['track'].'"';
            }
           
            $content = str_replace(array_keys($searchReplace), array_values($searchReplace), $content);
            unset($searchReplace);

            // save the url tags.
            if ($mustSave && $canSave) {
               
                foreach (self::$_trackingUrls[$campaign->campaign_id] as $urlData) {
                   
                    $urlModel = CampaignUrl::model()->findByAttributes(array(
                        'campaign_id' => (int)$campaign->campaign_id,
                        'hash'        => $urlData['hash'],
                    ));
                   
                    if (!empty($urlModel)) {
                        continue;
                    }
                   
                    $urlModel = new CampaignUrl();
                    $urlModel->campaign_id = $campaign->campaign_id;
                    $urlModel->destination = $urlData['url'];
                    $urlModel->hash = $urlData['hash'];
                    $urlModel->save(false);
                }
            }
        }
       
       
        $content = Yii::app()->hooks->applyFilters('campaign_content_after_transform_links_for_tracking', $content, $campaign, $subscriber, $list);
       
        // return transformed
        return $content;
    }
After you save, you can do:
PHP:
Yii::app()->hooks->addFilter('campaign_content_before_transform_links_for_tracking', function($emailContent, $campaign, $subscriber, $list){
//My code here
return $emailContent;
});
Test it and lemme know how it went.
 

Lakjin

Active Member
Test it and lemme know how it went.
Awesome, will do! One question, though.

How do I create an extension for MailWizz? Meaning, where do I throw this code into:
PHP:
Yii::app()->hooks->addFilter('campaign_content_before_transform_links_for_tracking', function($emailContent, $campaign, $subscriber, $list){//My code herereturn $emailContent;
});
Right now I'm directly modifying MailWizz files which is a big no no since they will be overwritten upon update. If I can create an extension with the code shown above, that would work best.
 

twisted1919

Administrator
Staff member
Always create extensions, because from extensions you can simply replace the entire application...
Here's an example:

PHP:
<?php defined('MW_PATH') || exit('No direct script access allowed');

class MyCampaignTagsExt extends ExtensionInit
{
    // name of the extension as shown in the backend panel
    public $name = 'My Campaign Tags';

    // description of the extension as shown in backend panel
    public $description = 'My Campaign Tags';

    // current version of this extension
    public $version = '1.0';

    // the author name
    public $author = 'Your name';

    // author website
    public $website = 'http://www.website.com/';

    // contact email address
    public $email = 'your.email@domain.com';

    // in which apps this extension is allowed to run
    public $allowedApps = array('*');

    // can this extension be deleted? this only applies to core extensions.
    protected $_canBeDeleted = true;

    // can this extension be disabled? this only applies to core extensions.
    protected $_canBeDisabled = true;

    // run the extension, ths is mandatory
    public function run()
    {
        Yii::app()->hooks->addFilter('campaign_content_before_transform_links_for_tracking', function($content, $campaign, $subscriber, $list){
            //My code here
            return $content;
        });
    }
}
Put this in a file called MyCampaignTagsExt.php then create a folder named my-campaign-tags and put the .php file in it and zip the folder then upload it into mailwizz as an extension.
 

Lakjin

Active Member
Question. How do I utilize MailWizz's connection to MySQL? I want to run some queries directly on the database in the extension.
 

Lakjin

Active Member
I finally got around to doing what you said -- I updated apps/common/components/helpers/CampaignHelper.php and created and activated the following extension:

PHP:
<?php defined('MW_PATH') || exit('No direct script access allowed');

class MyCampaignTagsExt extends ExtensionInit
{
    // name of the extension as shown in the backend panel
    public $name = 'My Campaign Tags';

    // description of the extension as shown in backend panel
    public $description = 'My Campaign Tags';

    // current version of this extension
    public $version = '1.0';

    // the author name
    public $author = 'Your name';

    // author website
    public $website = 'http://www.website.com/';

    // contact email address
    public $email = 'your.email@domain.com';

    // in which apps this extension is allowed to run
    public $allowedApps = array('*');

    // can this extension be deleted? this only applies to core extensions.
    protected $_canBeDeleted = true;

    // can this extension be disabled? this only applies to core extensions.
    protected $_canBeDisabled = true;

    // run the extension, ths is mandatory
    public function run()
    {
        Yii::app()->hooks->addFilter('campaign_content_before_transform_links_for_tracking', function($content, $campaign, $subscriber, $list){
                    $content = str_replace('[xxx]','yyy',$content);
            return $content;
        });
    }
}
However, [xxx] is not being replaced in emails.
 

Lakjin

Active Member
I don't know if it helps but if I do this, then it works:

PHP:
public static function transformLinksForTracking($content, Campaign $campaign, ListSubscriber $subscriber, $canSave = false)
 {
//code here
$content = StringHelper::decodeSurroundingTags($content);$list = $campaign->list;$mustSave = false;
Of course, we don't want to edit MailWizz files, so I tried the following as well, but it didn't work:

PHP:
public static function transformLinksForTracking($content, Campaign $campaign, ListSubscriber $subscriber, $canSave = false)
 {
$list = $campaign->list;
$content = Yii::app()->hooks->applyFilters('campaign_content_before_transform_links_for_tracking', $content, $campaign, $subscriber, $list);
$content = StringHelper::decodeSurroundingTags($content);$mustSave = false;
 
Top