We are implementing a 'One-Click Unsubscription' feature in Adobe Campaign Classic. So far, we have successfully customized the out-of-the-box web app provided by Adobe to handle unsubscriptions and track them programmatically.
Our next challenge is identifying which delivery was used by a recipient to unsubscribe. Specifically, we want the web app to retrieve the delivery code associated with the recipient's unsubscription click. This would allow us to track the types of deliveries prompting unsubscriptions and manage service-level unsubscriptions more effectively.
We want to:
Is there a straightforward way to include the delivery code in the web app's context or fetch it programmatically? Are there best practices for handling delivery-level unsubscriptions to make this process more efficient?
Any guidance or examples on better handling and tracking of One Click Unsubscription would be greatly appreciated!
Solved! Go to Solution.
Views
Replies
Total Likes
What you'd like to do is relatively easy to achieve. Be aware the post is a bit long as there are several steps to implement.
Also, the solution that will be described below is applicable if the unsubscription page is hosted by Adobe Campaign and is considered as a customisation of your Adobe Campaign solution as such, you will be responsible for its maintenance, etc...
if (NL.isEmpty(ctx.@secret))
logError("invalid secret...");
var secret = decryptString(ctx.@secret);
if (NL.isEmpty(secret))
logError("invalid secret...");
this.secret = JSON.parse(secret);
if (NL.isEmpty(this.secret))
logError("invalid secret...");
if (this.secret.method != ctx.@_method ||
this.secret.identifier != ctx.@_identifier ||
this.secret.key != ctx.recipient.@_key ) {
logError("invalid secret...");
}
if( String(ctx.recipient.@id) != decryptString(this.secret.identifier) )
logError("The webApp inputs are corrupted.");
// remove descenants @_key to protecte against sql injection
var descendantNode = ctx.recipient.descendants();
for (var i = 0; i < descendantNode.length(); i++) {
delete descendantNode[i].@_key;
delete descendantNode[i].@_operation;
}
var debug = true; // Should be set to false in Production
// Local variable from the context
var deliveryId = ctx.vars.deliveryId;
var messageId = ctx.vars.messageId
var recipientId = ctx.recipient.@id;
// Local variable for the tracking URL and tracking log
var logDate = formatDate(new getCurrentDate(), "%4Y-%2M-%2D %2H:%2N:%2S");
var landingPageName = "myUnsubNoClick"; // Should be changed to the internal name of the ACC unsubscription landing page
var source = getOption("NmsServer_URL") + "/webApp/"+ landingPageName + "?id=";
var label="Opt-out (1-Click)";
var type = 3; // opt-out
// Here you do manage your unsubscription as per your requirements. in standard if a service is provided (name)
nms.subscription.Unsubscribe(ctx.vars.service, ctx.recipient)
// Opt-out the recipient, adapt to your unsubscription requirements
// Please note that in this context, 2 new fields have been added unsubscribeDate and unsubscribeReason, adapt to your own unsubscription solution
xtk.session.Write(<recipient xtkschema="nms:recipient" _key="@id" id={recipientId} _operation="update" blackListEmail="1" unsubscribeDate={logDate} unsubscribeReason={label} />);
// If the delivery identifier is filled in, create a tracking log of type opt-Out
if (debug)
logInfo(landingPageName + " - deliveryId: " + deliveryId + " messageId: " + messageId + " recipientId: " + recipientId + " On " + logDate);
if (deliveryId != 0 && messageId != 0)
{
try
{
var createTrackingUrl = false;
var trackingUrlId = 0;
var xmlQuery = <queryDef schema="nms:trackingUrl" operation="select">
<select>
<node expr="@id"/>
</select>
<where>
<condition expr={ "[@delivery-id]=" + deliveryId} />
<condition expr={ "@source like '%" + landingPageName + "%'"} />
</where>
</queryDef>;
var qry = xtk.queryDef.create(xmlQuery);
var xmlTrackingUrl = qry.ExecuteQuery();
if (debug)
logInfo(landingPageName + " - query result: " + xmlTrackingUrl );
if (xmlTrackingUrl != '' )
{
for each (var trackingUrl in xmlTrackingUrl.trackingUrl)
{
trackingUrlId = trackingUrl.@id;
if (debug)
logInfo(landingPageName + " - trackingUrlId: " + trackingUrlId);
break;
}
}
// The URL for landing page link does not exist for the delivery, we need to create id
// This will be executed on the 1st 1-click usage for each delivery
if (trackingUrlId == 0)
{
xtk.session.Write(<trackingUrl _operation="insert" source={source} label={label} delivery-id={deliveryId} type={type} xtkschema="nms:trackingUrl"/>);
// Since we need its Id, we need to query the class again
var xmlQuery1 = <queryDef schema="nms:trackingUrl" operation="select">
<select>
<node expr="@id"/>
</select>
<where>
<condition expr={ "[@delivery-id]=" + deliveryId} />
<condition expr={ "@source like '%" + landingPageName + "%'"} />
</where>
</queryDef>;
var qry1 = xtk.queryDef.create(xmlQuery1);
var xmlTrackingUrl1 = qry1.ExecuteQuery();
if (debug)
logInfo("query result: " + xmlTrackingUrl1 );
if (xmlTrackingUrl1 != '' )
{
for each (var trackingUrl1 in xmlTrackingUrl1.trackingUrl)
{
trackingUrlId = trackingUrl1.@id;
if (debug)
logInfo(landingPageName+ " - trackingUrlId after save: " + trackingUrlId);
break;
}
}
}
// Now we should have a tracking URL identifier, write the tracking log
xtk.session.Write(<trackingLogRcp _operation="insert" recipient-id={recipientId} delivery-id={deliveryId} broadLog-id={messageId} url-id={trackingUrlId} logDate={logDate} xtkschema="nms:trackingLogRcp"/>);
}
catch( e )
{
logError("An error occurs in landing page " + landingPageName + " - register tracking): " + e);
throw e;
}
}
Precedence: bulk
List-Unsubscribe-Post: List-Unsubscribe=One-Click
List-Unsubscribe: <<%@ include option='NmsServer_URL' %>/webApp/myUnsubNoClick?id=<%=escapeUrl(recipient.cryptedId)%>&_deliveryId=<%= delivery.id %>&_messageId=<%= message.id %>&_service=newsletter >, <mailto:<%= provider.errorAddress !='' ? provider.errorAddress:delivery.mailParameters.errorAddress %>?subject=unsubscribe<%=escape(message.mimeMessageId) %>>
Tracking URL created once
Tracking Logs
Tracking Indicators
Before – Active Subscription
After – Active Subscription
After – History Subscription
Hope this helps,
Denis.
Hello,
One way could be to include the delivery id in the unsubscription link parameters and use it in the web app to read delivery code.
You can refer to delivery id with <%= delivery.id %>
Regards
Tero
Views
Replies
Total Likes
What you'd like to do is relatively easy to achieve. Be aware the post is a bit long as there are several steps to implement.
Also, the solution that will be described below is applicable if the unsubscription page is hosted by Adobe Campaign and is considered as a customisation of your Adobe Campaign solution as such, you will be responsible for its maintenance, etc...
if (NL.isEmpty(ctx.@secret))
logError("invalid secret...");
var secret = decryptString(ctx.@secret);
if (NL.isEmpty(secret))
logError("invalid secret...");
this.secret = JSON.parse(secret);
if (NL.isEmpty(this.secret))
logError("invalid secret...");
if (this.secret.method != ctx.@_method ||
this.secret.identifier != ctx.@_identifier ||
this.secret.key != ctx.recipient.@_key ) {
logError("invalid secret...");
}
if( String(ctx.recipient.@id) != decryptString(this.secret.identifier) )
logError("The webApp inputs are corrupted.");
// remove descenants @_key to protecte against sql injection
var descendantNode = ctx.recipient.descendants();
for (var i = 0; i < descendantNode.length(); i++) {
delete descendantNode[i].@_key;
delete descendantNode[i].@_operation;
}
var debug = true; // Should be set to false in Production
// Local variable from the context
var deliveryId = ctx.vars.deliveryId;
var messageId = ctx.vars.messageId
var recipientId = ctx.recipient.@id;
// Local variable for the tracking URL and tracking log
var logDate = formatDate(new getCurrentDate(), "%4Y-%2M-%2D %2H:%2N:%2S");
var landingPageName = "myUnsubNoClick"; // Should be changed to the internal name of the ACC unsubscription landing page
var source = getOption("NmsServer_URL") + "/webApp/"+ landingPageName + "?id=";
var label="Opt-out (1-Click)";
var type = 3; // opt-out
// Here you do manage your unsubscription as per your requirements. in standard if a service is provided (name)
nms.subscription.Unsubscribe(ctx.vars.service, ctx.recipient)
// Opt-out the recipient, adapt to your unsubscription requirements
// Please note that in this context, 2 new fields have been added unsubscribeDate and unsubscribeReason, adapt to your own unsubscription solution
xtk.session.Write(<recipient xtkschema="nms:recipient" _key="@id" id={recipientId} _operation="update" blackListEmail="1" unsubscribeDate={logDate} unsubscribeReason={label} />);
// If the delivery identifier is filled in, create a tracking log of type opt-Out
if (debug)
logInfo(landingPageName + " - deliveryId: " + deliveryId + " messageId: " + messageId + " recipientId: " + recipientId + " On " + logDate);
if (deliveryId != 0 && messageId != 0)
{
try
{
var createTrackingUrl = false;
var trackingUrlId = 0;
var xmlQuery = <queryDef schema="nms:trackingUrl" operation="select">
<select>
<node expr="@id"/>
</select>
<where>
<condition expr={ "[@delivery-id]=" + deliveryId} />
<condition expr={ "@source like '%" + landingPageName + "%'"} />
</where>
</queryDef>;
var qry = xtk.queryDef.create(xmlQuery);
var xmlTrackingUrl = qry.ExecuteQuery();
if (debug)
logInfo(landingPageName + " - query result: " + xmlTrackingUrl );
if (xmlTrackingUrl != '' )
{
for each (var trackingUrl in xmlTrackingUrl.trackingUrl)
{
trackingUrlId = trackingUrl.@id;
if (debug)
logInfo(landingPageName + " - trackingUrlId: " + trackingUrlId);
break;
}
}
// The URL for landing page link does not exist for the delivery, we need to create id
// This will be executed on the 1st 1-click usage for each delivery
if (trackingUrlId == 0)
{
xtk.session.Write(<trackingUrl _operation="insert" source={source} label={label} delivery-id={deliveryId} type={type} xtkschema="nms:trackingUrl"/>);
// Since we need its Id, we need to query the class again
var xmlQuery1 = <queryDef schema="nms:trackingUrl" operation="select">
<select>
<node expr="@id"/>
</select>
<where>
<condition expr={ "[@delivery-id]=" + deliveryId} />
<condition expr={ "@source like '%" + landingPageName + "%'"} />
</where>
</queryDef>;
var qry1 = xtk.queryDef.create(xmlQuery1);
var xmlTrackingUrl1 = qry1.ExecuteQuery();
if (debug)
logInfo("query result: " + xmlTrackingUrl1 );
if (xmlTrackingUrl1 != '' )
{
for each (var trackingUrl1 in xmlTrackingUrl1.trackingUrl)
{
trackingUrlId = trackingUrl1.@id;
if (debug)
logInfo(landingPageName+ " - trackingUrlId after save: " + trackingUrlId);
break;
}
}
}
// Now we should have a tracking URL identifier, write the tracking log
xtk.session.Write(<trackingLogRcp _operation="insert" recipient-id={recipientId} delivery-id={deliveryId} broadLog-id={messageId} url-id={trackingUrlId} logDate={logDate} xtkschema="nms:trackingLogRcp"/>);
}
catch( e )
{
logError("An error occurs in landing page " + landingPageName + " - register tracking): " + e);
throw e;
}
}
Precedence: bulk
List-Unsubscribe-Post: List-Unsubscribe=One-Click
List-Unsubscribe: <<%@ include option='NmsServer_URL' %>/webApp/myUnsubNoClick?id=<%=escapeUrl(recipient.cryptedId)%>&_deliveryId=<%= delivery.id %>&_messageId=<%= message.id %>&_service=newsletter >, <mailto:<%= provider.errorAddress !='' ? provider.errorAddress:delivery.mailParameters.errorAddress %>?subject=unsubscribe<%=escape(message.mimeMessageId) %>>
Tracking URL created once
Tracking Logs
Tracking Indicators
Before – Active Subscription
After – Active Subscription
After – History Subscription
Hope this helps,
Denis.
Views
Likes
Replies