How to enable Piwik analytics on Discourse


(Erlend Sogge Heggen) #1

Piwik is an open source analytics platform. Users can choose between self-hosting or paying for professional hosting in the cloud.

To enable Piwik analytics on your Discourse forum, all you have to do is insert your tracking code in the right place.

Valid for Piwik API v1.5 and newer

Go to Admin / Customize / CSS/HTML / </body> and insert:

<script type="text/javascript">
    var _paq = _paq || [];
    _paq.push(["enableLinkTracking"]);
    (function() {
        var u=(("https:" == document.location.protocol) ? "https" : "http") + "://<!-- URL HERE -->";
        _paq.push(["setTrackerUrl", u+"piwik.php"]);
        _paq.push(["setSiteId", "<!-- WEBSITE ID HERE -->"]);
        var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
        g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
    })();
</script>
<noscript><p><img src="//<!-- URL HERE -->/piwik.php?idsite=<!-- SITEID HERE -->" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->

Be sure to change these two:

  1. <!-- URL HERE --> should be replaced with your website’s URL
  2. <!-- WEBSITE ID HERE --> should be replaced with your website ID.

Then go to to Admin / Customize / CSS/HTML / </head> and insert:

<script type="text/discourse-plugin" version="0.2">
    api.onPageChange((url, title) => {
        if (_paq) {
            var currentUser = api._currentUser;
            _paq.push(['setCustomVariable', 1, 'Anonymous', !currentUser, 'visit']);
            _paq.push(["setCustomUrl", url]);
            _paq.push(["setDocumentTitle", title]);
            _paq.push(["trackPageView"]);
        }
    });
</script>
<!-- End Piwik Code -->

That’s it!

Thanks to @mattab & @brahn for updates


Support for Piwik Analytics as an alternative to Google Analytics
How to anonymize Google Analytics?
showOpLikes - How to enable?
Javascript inserted into Footer from Discourse UI not working
Sidekiq: Uncaught TypeError: Cannot read property 'current' of undefined
Support for Piwik Analytics as an alternative to Google Analytics
Accessibility: Signal changes to screen readers on navigation
(Brahn) #2

Since I upgraded from 1.3 to 1.4 I am now seeing this deprecation notice in the console:

WARNING: DEPRECATION: 'Discourse.PageTracker' is deprecated, import the module.

As mentioned here and here this is to be expected but I have no idea how to import the module. Simply doing import PageTracker from 'discourse/lib/page-tracker'; doesn’t work, is there a trick to doing this in a <script> element?


(Robin Ward) #3

This should probably be a proper plugin rather than Javascript you insert on your pages. In a plugin that uses ES6 you can import the module directly.


(Brahn) #4

The idea of a plugin was floated on a previous topic but:

:smiley:

I guess things have changed enough to push this further into plugin territory? I did a little research (google some stuff) and it seems there are ways to support importing within a <script> like es6-module-loader if something like that makes sense for Discourse?


(Robin Ward) #5

You can access the module via other means but I really don’t recommend it. The correct way to do it is to make a plugin for this :smile:


(Kane York) #6

Well, require('discourse/lib/page-tracker').default really isn’t that hard :wink:

And if we want people to be using es6… how about adding a es6 JS customizations tab?


(Robin Ward) #7

It’s not that it’s hard it’s that I can’t guarantee that will continue to work in the future. Currently ES6 modules transpile to AMD but it’s quite likely for performance reasons in the future we’ll do a different kind of compilation.

Please don’t suggest that approach to people as I don’t want a backwards compatibility layer for that.

As for ES6 JS customizations – that could work. But it’s also considerably better off as a plugin as I’ve suggested a couple of times now.


(Brahn) #8

I’m sensing a theme… something about doing it as a plugin :smile:
For now the <script> is still working of course. I will try to make an opportunity to work on a plugin but if someone else gets there first I am cool with that too!


(Sam Saffron) #9

Long term we got to think of a clean, forward compatible way adding JS customizations that has access to modules and so on.

Our business and standard customer can not install plugins and customizations can easily be installed without needing container rebuilds and so on.

It is a problem we got to solve.


(Robin Ward) #10

I’ve given it some thought and I don’t think it’s a good idea to let snippets of JS access modules. It just sets us up for so much pain in the future. Ignoring the highly likely case that we will use a different transpiled module format such as bundling to avoid the loader in ember, it means that we have to worry about people reaching into our internals and then they will be very easy to break.

Instead what I think would be better is to release an extension API for plugins. For example:

var api = new DiscourseExtensionAPI('1.0');
api.on('changePage', function(url) {
  // whatever
});

Passing the version as a string would allow us to perform backwards compatibility operations if required.


(Brahn) #11

With the new API in v1.5 I have updated my piwik code to work as follows.

In </body> is the piwik script loader as before:

<script type="text/javascript">
    var _paq = _paq || [];
    _paq.push(["trackPageView"]);
    _paq.push(["enableLinkTracking"]);
    (function() {
        var u=(("https:" == document.location.protocol) ? "https" : "http") + "://<!-- URL HERE -->";
        var currentUser = PreloadStore.get('currentUser');
        _paq.push(['setCustomVariable', 1, 'Anonymous', !currentUser, 'visit']);
        _paq.push(["setTrackerUrl", u+"piwik.php"]);
        _paq.push(["setSiteId", "<!-- WEBSITE ID HERE -->"]);
        var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
        g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
    })();
</script>

Then the new bit in the </head>:

<script type="text/discourse-plugin" version="0.2">
    api.onPageChange((url, title) => {
        if (_paq) {
            _paq.push(["setCustomUrl", url]);
            _paq.push(["setDocumentTitle", title]);
            _paq.push(["trackPageView"]);
        }
    });
</script>

This works well for me so far and no more deprecation message!


(Erlend Sogge Heggen) #12

Thanks for the update. Are URL HERE and WEBSITE ID HERE still the only two custom inputs you need to worry about?


(Brahn) #13

I believe so, yes. The only change is calling the onPageChange with the new api.

As a side note, does anyone know if there is a way to extend or replace the _google_analytics.html.erb etc with something custom? The motivation being to get a more complete integration/replacement for ga.


#14

Doesn’t look like this portion works anymore? I don’t see the custom variable sent anymore to Piwik. Anyone got any idea?

I managed to get it working by updating the above to the current standard changing it to:

var currentUser = api.get('currentUser');

I’ve updated the wiki post at the top to reflect this along with another quick bug fix which caused duplicate hits to be done upon initial page load.


(Erlend Sogge Heggen) #15

Thanks for the edit!

@mattab could you please review this latest revision and make sure we’re giving the right instructions here?


(Brahn) #16

Which version are you on @Mooash ? On v1.5.4 I get an error in the console “api is not defined” with this change.


#17

I’m on 1.6.0.beta12 (latest stable).

The duplicate tracking on initial page load was due to triggering the below JS trigger twice (once in the header, once in the footer).

_paq.push(["trackPageView"]);

Removing it in the footer fixed the issue since it shouldn’t be triggered there anyway.


(Brahn) #18

After upgrade to 1.6.0 I was still having issues. Moving the setCustomVariable into the Admin / Customize / CSS/HTML / </head> is better. Also, api.get doesn’t do anything for me but this works:

  var currentUser = api._currentUser;
  _paq.push(['setCustomVariable', 1, 'Anonymous', !currentUser, 'visit']);

I have no idea just how appropriate this is so I don’t want to update the wiki until someone can confirm!


#19

Yeah sorry, I’ve got that currentUser var set in the </head> section since it makes more sense to be to run this every time a page changes. This is mostly to ensure if a user logs in the var gets updated for their session.


#20

I’m using the code from the official forum but those custom variables never worked, so I tried yours and it’s not working either…

There is no data for this report.