Sanford Whiteman

a.k.a. “that guy” from the Marketo Community

who also blogs at https://blog.teknkl.com

and has created some Marketo apps

such as FlowBoost.

A TEKNKL::BLOG TEASER

(NOT FOR TODAY!)*

*Unless we have spare time

h/t NEAL FORD's

“SUCK/ROCK DICHOTOMY”

Sandy'S

“CONTINUOUS SUCKROCKING” METHODOLOGY

NAME THAT SMART CAMPAIGN!

Solve

for

?????????????????

Fixing

known visitor html

  • KV HTML = “If Known Visitor, Show Custom HTML”

  • KV HTML is a mini-form (with Download <button> or links if you want) instead of standard form layout

  • will not kick in if the lead's First Name or Last Name is empty (!!!)

  • but will kick in if the lead's Email Address is empty (think about it)

ALTERNATIVE FACT:

ACTUAL FACTUAL:

  • You need a strategy for dealing with empty names 
  • If you want people to be able to correct an invalid Email Address, consider proxy fields and blanking out First and/or Last Name
  • Proxy fields are a good investment anyway (only way to let someone change their email via Marketo form!)

The OTHER

KV HTML BUG

  • KV HTML is still a form and will result in a Filled Out Form activity
  • But native Auto-Fill for hidden fields is disabled (!!!)
  • UTMs (and other query params) will not be tracked
  • much confusion ensues when all revisits appear to be Direct

the suckitude

the rockness

  • You need to (re)build query param, cookie, and referrer stuff in JS
  • Be nice if this were documented
  • You get to (re)build query param, cookie, and referrer stuff in JS... and make it better!
  • The Forms JS API makes it easy to hook into addHiddenFields()
  • Replace all your UTM tracking with smarter JS
var hiddenFieldMap = {
  Last_Result__c: {
     /* this field is filled from the current query parameter `utm_results` */
     channel: "currentQuery",
     selector: "utm_results"
  },
  Field_From_Cookie: {
     /* this one is filled from the full value of the saved cookie `ahoy_visitor` */
     channel: "cookie",
     selector: "ahoy_visitor",
  },
  Field_From_Cookie_As_Object: {
     /* this one is filled from a *single property* of a cookie `utm_history_1` 
        that was stored as a JSON object */
     channel: "cookie",
     selector: "utm_history_1",
     property: "utm_zz"
  },
  Another_Field_From_Constant: {
     /* this one is always set to the same value whenever somebody hits this page*/
     channel: "constant",
     selector: "Always use this value."
  },
  Field_From_Query_First_Then_Cookie: [
     /* this one first looks in the current query param `utm_campaign` and if
        that value was not found, moves on to the saved cookie `last_utm_campaign` */
     {
        channel: "currentQuery",
        selector: "utm_campaign"
     },
     {
        channel: "cookie",
        selector: "last_utm_campaign"
     }
  ],
  Field_From_Referrer_URL: {
     /* this one uses the query param `search_results` from the document.referrer URL */
     channel: "referrerQuery",
     selector: "search_results"
  }
   };

   /* --- NO NEED TO EDIT BELOW THIS LINE! --- */

   var currentCookies = FormsPlus.util.Cookies.getJSON(),
  currentQuery = FormsPlus.util.URI.URI().search(true),
  referrerQuery = FormsPlus.util.URI.URI(document.referrer||"").search(true);

   var hiddenFields = Object.keys(hiddenFieldMap)
 .reduce(function(acc, field) {
    
   var fieldDescriptor = hiddenFieldMap[field],
       allDescriptors = [].concat(fieldDescriptor);
    
   allDescriptors
     .reverse()
     .forEach(function(descriptor) {
       switch (descriptor.channel) {
         case "cookie":
           if(currentCookies[descriptor.selector] && descriptor.property) {

  /* ... and so on... */
          

better auto-Fill JS

ThE

LEAD LOOKUP FORM

PATTERN

leads hit your subscription center

without clicking a tracked link

or submitting a form (in that browser).

 

What do you show 'em?

This ISn't the same as PRE-FILL!

Pre-Filling known lead data from

Marketo can work on any form

(or even a non-Marketo one)...

... but always depends on

the lead's web session or pageview

being associated with their lead

when the form renders.

  • might “unsubscribe all” if don't see they're already subbed

  • same for any lead fields and especially opt-in fields

  • better: retrieve their current values for update

Lead lookup Form​: GENERAL DESIGN

After standard Marketo form onSuccess(),

start checking contents of hidden IFRAME

Poll IFRAME every N seconds

(up to max retries) until associated

Return data to parent window

on email match and run JS API setValues()

Lead lookup Form JS

  form.onSuccess(function(vals, originalThankYouURL) {

    var thankYouURL = "/Lead-Lookup-Frame-v1.0.0",
      thankYouIframe = document.createElement("iframe"),
      totalAttempts = 10,
      delayInterval = 1000;
    
    thankYouIframe.src = thankYouURL;
    thankYouIframe.style.display = "none";
    thankYouIframe.addEventListener("load", function(e) {
      if (window.mktoAssociatedLead.Email != vals.Email) {
        if (--totalAttempts) {
          setTimeout(function() {
            thankYouIframe.contentWindow.document.location.reload();
          }, delayInterval);
        } 
      } else {
        form.setValues({
          Active_Subscriptions_Indicator__c : mktoAssociatedLead.memberOfLists
        });
        emailEl.disabled = true; // lock the current Email
        buttonEl.disabled = false; // reactivate the button so lead can update
        buttonEl.textContent = "Update Subscriptions";
        form.onSuccess();
      }
    });

    /* ... and so on... */

OKAY, BUT... what about malice?

I agree, great bad movie.

Ah, right.

the

“self-service auth code”

enhancement

self-service auth code =

Marketo unique code

Lead lookup Form​ w/auth

After standard Marketo form onSuccess(),

check contents of IFRAMEd pURL

pURL responds immediately (no polling needed)

Return data to parent window

and run JS API setValues()

  form.onSuccess(function(vals, originalThankYouURL) {
    var thankYouURL = "/Lead-Lookup-Frame-pURL-v1.0.0/" + vals.selfServiceFormAuthCode,
      thankYouIframe = document.createElement("iframe"),
      totalAttempts = 1,
      delayInterval = 1000;
    
    thankYouIframe.src = thankYouURL;
    thankYouIframe.style.display = "none";
    thankYouIframe.addEventListener("load", function(e) {
      if (window.mktoAssociatedLead && mktoAssociatedLead.Email != vals.Email) {
        alert("Sorry, we could not find a matching lead record." );
        buttonEl.disabled = false;
        buttonEl.textContent = "Lookup Subscriptions";
      } else if (window.mktoAssociatedLead) {
        form.setValues({
          Active_Subscriptions_Indicator__c : mktoAssociatedLead.memberOfLists
        });
        [emailEl,authCodeEl].forEach(function(el){
          el.disabled = true; // disable Email and Auth Code inputs
        });        
        buttonEl.disabled = false;
        buttonEl.textContent = "Update Subscriptions";
        form.onSuccess();
      }
    });

    /* ... and so on... */

AUTH'D lookup Form​ JS

Yes, there's a sort-of-catch still.

(Wanna guess?)

ThE

RESOURCE LEAD

PATTERN

RESOURCE LEAD

  • concept taken from “groupware” apps (Exchange, Notes, etc.)
  • in groupware, a non-human object stored in a company directory alongside people objects

RESOURCE LEAD

  • you book a resource and it keeps track of who is (and who has been) using it
  • in Marketo, similar notion applied to Programs

RESOURCE LEAD

  • you book a resource and it keeps track of who is (and who has been) using it
  • in Marketo, concept can be applied to programs as resources
  • allows you to count members of an event program as a Score field on a resource lead

(olde tyme

record scratch photo

because all the other

stock photos were

about “scratching”

as in DJing skillz)

  • not the easiest way to do things like event caps (that would be FlowBoost or, heh, comparable services)
  • but it's the only way that uses Marketo alone (think compliance, DPAs, etc.)
  • and it's a deeply strange & cool envelope-pusher with Marketo technology
  • if you get all of it, you can retire at the top of your game :)

RESOURCE LEAD:

The innocent beginning

RESOURCE LEAD:

The innocent beginning

RESOURCE LEAD:

BIND the RESOURCE TO THE PRogram

RESOURCE LEAD:

Now it gets crazy

  • we need to update fields on a program's resource lead based on activity in the program
  • we need to read the resource lead's fields from a browser, without being Munchkin-associated as the resource lead (and without being associated at all)

RESOURCE LEAD:

reading resource lead data

  • wouldn't it be great if there were a browser-ready web service built into Marketo?
  • is XML browser-ready enough for ya?
  • can you make a Marketo Landing Page that's valid XML?

RESOURCE LEAD:

reading resource lead data

  • yes, a Marketo Landing Page can be valid XML (not just HTML)!
  • uses a mktoString variable trick
<!DOCTYPE html>
<html>
<meta class="mktoString" allowHtml="true" id="commentOpen" mktoName="commentOpen">
<meta class="mktoString" allowHtml="true" id="commentClose" mktoName="commentClose">
${commentOpen}
<head>
<title></title>
<meta charset="utf-8">
</head>
${commentClose}
<body>
<lead>
  <attributes>
    <score>{{lead.Lead Score}}</score>
  </attributes>
</lead>
</body>
</html>

XML-IFIED MkTo TEMPLATE + LP

Sample xml response

<!DOCTYPE html>
<html>

<!--
<head>
<title></title>
<meta charset="utf-8"><meta name="robots" content="index, nofollow">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" >
<link rel="icon" href="/favicon.ico" type="image/x-icon" >

<style>.mktoGen.mktoImg {display:inline-block; line-height:0;}</style>
</head>
-->
<body id="bodyId">

<lead>
  <attributes>
    <score>126</score>
  </attributes>
</lead>

</body>
</html>

RESOURCE LEAD:

reading resource lead data

  • now you have an XML service that you can read from the browser (via Ajax) and from the server (via webhook)
  var getResourceLead = new XMLHttpRequest;
      getResourceLead.open("GET","/XHTML-Lead-Data-Source/Programid4431Resourcelead");
      getResourceLead.responseType = "document";
      getResourceLead.onabort = getResourceLead.onerror = restoreCookie;

      getResourceLead.onload = function(){
        var counterFromResourceLead = this.responseXML.querySelector("score");

        MktoForms2.whenReady(function(form){
          var formEl = form.getFormElem()[0],
          formCounterOutput = formEl.querySelector("#currentEventCount");
          formCounterOutput.textContent = counterFromResourceLead.textContent;
        });
      };

      getResourceLead.send();

JS TO get XML counter INTO PAGE

  var getResourceLead = new XMLHttpRequest;
      getResourceLead.open("GET","/XHTML-Lead-Data-Source/Programid4431Resourcelead");
      getResourceLead.responseType = "document";
      getResourceLead.onabort = getResourceLead.onerror = restoreCookie;

      getResourceLead.onload = function(){
        var counterFromResourceLead = this.responseXML.querySelector("score");

        restoreCookie();

        MktoForms2.whenReady(function(form){
          var formEl = form.getFormElem()[0],
          formCounterOutput = formEl.querySelector("#currentEventCount");
          formCounterOutput.textContent = counterFromResourceLead.textContent;
        });
      };

      var restoreCookie = function(){
         if (originalEndLeadCookieValue) {
           FormsPlus.util.Cookies.set(
             cookieName,
             originalEndLeadCookieValue,
             cookieOptions
           );
         }
      };

      FormsPlus.util.Cookies.remove(
         cookieName,
         cookieOptions
      );

      getResourceLead.send();

(OK, JS IS more like this)

Or you can redirect to an “Event Full” page if over your max, or anything else you want to do with the info...

if (countFromResourceLead > {{my.Registrant Limit}})
  document.location.href = "/event-is-full.html";

You can just display the current count in the page...

On the server side, you can also bring the Resource Lead's data onto regular leads using a webhook...

RESOURCE LEAD:

WrITING resource lead data

  • semi-secret /save endpoint can be POSTEd from a webhook
  • this allows cross-lead updates (program member lead ⇨ resource lead)

This webhook notifies the Resource Lead that there's action in the program...

As

human

leads

register...

... the

Resource

Lead's

score

is bumped!

RESOURCE LEAD:

The circle is complete(-ly crazY)

  • more than just program counts and event caps
  • you can also use the Resource Lead to schedule global outbound connections to other sources (think RSS)
  • you can subscribe people to a Smart List of all Resource Leads to see totals, or Send Alerts containing Resource Lead aggregate data

DONE FOR TODAY!

Remember: go to https://blog.teknkl.com

DONE FOR TODAY!

Marketo MUG 2019-01 Recorded

By Sanford Whiteman, TEKNKL

Private

Marketo MUG 2019-01 Recorded