Link rewriting for affiliate codes

是的,这是可行的。让我们从简单的开始。首先,将以下内容添加到新主题组件的“头部(header)”标签页中。

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

</script>

这是做什么的?这是一种由 Discourse 处理的特殊脚本标签。有什么好处呢?它允许你访问 插件 API 的方法。

你想给某些链接附加联盟代码。这意味着你需要修改帖子的内容。如果你查看插件 API,会发现确实有对应的方法。

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

使用方法如下:

api.decorateCookedElement(
  element =\u003e {
    // 在这里执行你的操作。
    // 此处传入的 element 是已渲染帖子的 HTML 节点
  },
  {
    // 选项放在这里
  }
);

这是你需要包裹代码的容器,以便在帖子渲染时触发它。那么,让我们尝试一下。

我们将你的代码原样添加进去:

api.decorateCookedElement(
  element =\u003e {
++  // 将下面的 "my-affiliate-id" 替换为你实际的联盟 ID
++  const aid = "my-affiliate-id";
++
++  // 仅在链接中尚未包含联盟 ID 时,追加带联盟 ID 的斜杠
++  const goglinks = document.querySelectorAll('a[href*="gog.com"]');
++  goglinks.forEach(function (el) {
++    if (!el.href.includes("pp=")) {
++      el.href = el.href.replace(/\?.*$/, "") + "?pp=" + aid;
++    }
++  });
  },
  {
    // 选项放在这里
  }
);

这样能行吗?可以,但它也在做一些多余的工作,因为我们是在查询整个 document 中的链接,而不是针对帖子元素。那么如何修复呢?

还记得我们传入方法的 element 参数吗?这时候它就派上用场了。

我们不再查询整个文档,而是查询我们要针对的元素。因此,我们将这行代码:

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

改为:

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

最终结果如下:

api.decorateCookedElement(
  element =\u003e {
    // 将下面的 "my-affiliate-id" 替换为你实际的联盟 ID
    const aid = "my-affiliate-id";

    // 仅在链接中尚未包含联盟 ID 时,追加带联盟 ID 的斜杠
--  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;
      }
    });
  },
  {
    // 选项放在这里
  }
);

现在效果很好,但我们还可以再优化一下。由于你只是给链接添加联盟 ID,因此在编辑器(composer)中并不需要触发这段代码。这时方法选项就派上用场了。

你可以传递给方法的一个选项是 onlyStream

它的作用是什么?它告诉该方法,你只希望在帖子在主题内渲染时才执行此代码。让我们添加这个选项。

api.decorateCookedElement(
  element =\u003e {
    // 将下面的 "my-affiliate-id" 替换为你实际的联盟 ID
    const aid = "my-affiliate-id";

    // 仅在链接中尚未包含联盟 ID 时,追加带联盟 ID 的斜杠
    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 type="text/discourse-plugin" version="0.8.42">
api.decorateCookedElement(
  element =\u003e {
    // 将下面的 "my-affiliate-id" 替换为你实际的联盟 ID
    const aid = "my-affiliate-id";

    // 仅在链接中尚未包含联盟 ID 时,追加带联盟 ID 的斜杠
    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>

然后将其添加到你的组件的“头部(header)”标签页中,就完成了。我在这个示例中使用了 GOG,因为这是你询问的对象,但只要你知道 URL 结构,这种模式可以用于任何联盟计划。

11 个赞