Link rewriting for affiliate codes

Yes, it’s possible. Let’s start with something simple. First, you add this to the header tab of a new theme component.

<script type="text/discourse-plugin" version="0.8.42">

</script>

What does this do? It’s a special type of script tags that get handled by Discourse. What’s the benefit? It allows you to access the plugin API methods.

You want to append an affiliate code to some links. So, that means that you want to modify the contents of posts. If you check the plugin API, you’ll see that there is indeed a method for that.

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/lib/plugin-api.js#L224-L262

You use it like this.

api.decorateCookedElement(
  element => {
    // do your work here. 
    // element passed here is the HTML node for cooked post
  },
  {
    // options go here
  }
);

This is the wrapper that you need to put your code in to have it fire when a post is rendered. So, let’s try that.

We take your code and add it as is

api.decorateCookedElement(
  element => {
++  // Change "my-affiliate-id" below to your actual affiliate id
++  const aid = "my-affiliate-id";
++
++  // Append slash with affiliate id, only if an affiliate ID is not found in the link yet
++  const goglinks = document.querySelectorAll('a[href*="gog.com"]');
++  goglinks.forEach(function (el) {
++    if (!el.href.includes("pp=")) {
++      el.href = el.href.replace(/\?.*$/, "") + "?pp=" + aid;
++    }
++  });
  },
  {
    // options go here
  }
);

Does this work? Yes, but it’s also doing some redundant work since we’re querying the document for the links instead of the post element. So, how do you fix that?

Remember the element argument that we passed in the method? Well, this is when it comes in handy.

Instead of querying the document, we query the element we want to target. So, we change this.

const goglinks = document.querySelectorAll('a[href*="gog.com"]');

to this

const goglinks = element.querySelectorAll('a[href*="gog.com"]');

and here’s what we end up with

api.decorateCookedElement(
  element => {
    // Change "my-affiliate-id" below to your actual affiliate id
    const aid = "my-affiliate-id";

    // Append slash with affiliate id, only if an affiliate ID is not found in the link yet
--  const goglinks = document.querySelectorAll('a[href*="gog.com"]');
++  const goglinks = element.querySelectorAll('a[href*="gog.com"]');
    goglinks.forEach(function (el) {
      if (!el.href.includes("pp=")) {
        el.href = el.href.replace(/\?.*$/, "") + "?pp=" + aid;
      }
    });
  },
  {
    // options go here
  }
);

This works great now, but we can still improve it a little bit. Since you’re only adding an affiliate id to links, you don’t need this to fire when you’re in the composer. That’s where the method options come in handy.

One of the options you can pass to the method is onlyStream

What does it do? It tells the method that you only want this code to fire when the post is rendered inside a topic. Let’s add that option.

api.decorateCookedElement(
  element => {
    // Change "my-affiliate-id" below to your actual affiliate id
    const aid = "my-affiliate-id";

    // Append slash with affiliate id, only if an affiliate ID is not found in the link yet
    const goglinks = element.querySelectorAll('a[href*="gog.com"]');
    goglinks.forEach(function (el) {
      if (!el.href.includes("pp=")) {
        el.href = el.href.replace(/\?.*$/, "") + "?pp=" + aid;
      }
    });
  },
  {
++  onlyStream: true
  }
);

So, now the only thing left to do is put this code in the special script tag we talked about earlier.

<script type="text/discourse-plugin" version="0.8.42">
api.decorateCookedElement(
  element => {
    // Change "my-affiliate-id" below to your actual affiliate id
    const aid = "my-affiliate-id";

    // Append slash with affiliate id, only if an affiliate ID is not found in the link yet
    const goglinks = element.querySelectorAll('a[href*="gog.com"]');
    goglinks.forEach(function (el) {
      if (!el.href.includes("pp=")) {
        el.href = el.href.replace(/\?.*$/, "") + "?pp=" + aid;
      }
    });
  },
  {
    onlyStream: true
  }
);
</script>

Then add it to the header tab of your component, and you’re all set. I used GOG in this example because that’s what you asked about, but this pattern can be used for any affiliate program as long as you know the URL structure.

11 Likes