Jump to content

Recommended Posts

Posted

I'm trying to add additional logic to my site to better track logged in users vs non logged in user activity.   If you set a gtag with the user_id name, it will tie that session to a specific account.  If I add the Analytics code to the Global Template in a theme, I can use something like:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXXXX-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('set', {'user_id': '{member="name"}'});
  gtag('js', new Date());
  gtag('config', 'UA-XXXXXXXXXX-1');

It would return the user_id as the username of the currently logged in user.  

However that code does not work if I move it to where it SHOULD go within the Google Analytics code in the Intergrations section.  Since that area does not have access to template logic, it obviously does not work.  

Is there a way I can return the logged in username via JS?  

Posted

I tinkered with the idea in the form of a plugin, but couldn't quite reach the finish line due to having other obligations take priority.  Its just a few steps to reproduce, so I'll share the progress here in case anyone wants to pick up the baton:

  • Create a new plugin
  • Add a setting where we can save new field data
    image.thumb.png.9652b68fd3db5d19fba2ff8c17f28c75.png
     
  • In \plugins\<thenewplugin>\dev\settings.php, Create a Stack form element so that you can edit the plugin settings to add new lines.
    $form->add( new \IPS\Helpers\Form\Stack( 'addGtags_add_line', \IPS\Settings::i()->addGtags_add_line ?? array(), FALSE, array(  'stackFieldType' => 'Text', 'removeEmptyValues' => FALSE ), NULL, NULL, NULL, 'addGtags_add_line' ));
    image.thumb.png.8527ab8f00ec5d0362e8ef0514bca685.png
     
  • Optionally display this field on the GA Form by hooking into \IPS\Helpers\Form::__construct()
    image.thumb.png.08063560007dffd99cbf993a29f555ba.png
     
    if(\IPS\Request::i()->app === 'core'
        && \IPS\Request::i()->module === 'applications'
        && \IPS\Request::i()->controller === 'enhancements'
        && \IPS\Request::i()->id === 'core_GoogleAnalytics' ){
        $this->add( new \IPS\Helpers\Form\Stack( 'addGtags_add_line', \IPS\Settings::i()->addGtags_add_line ?? array(), FALSE, array(  'stackFieldType' => 'Text', 'removeEmptyValues' => FALSE ), NULL, NULL, NULL, 'addGtags_add_line' ));
    }
  • Create a Theme hook for core front: global -> globalTemplate
    • CSS Selector:  html
    • Action:  Insert content before the chosen elements(s);
      • We want to override the ga_code setting as early as possible in the template
    • Content:
      {{$stringFields = implode(' ', \IPS\Settings::i()->addGtags_add_line);}}
      
      {{ \IPS\Settings::i()->ga_code = preg_replace('/(.*\bgtag\W+\w+\W+\w+-\d+-\d+\W+;)/i', '\1' . $stringFields, \IPS\Settings::i()->ga_code); }}
      
      Select the UA-XXXXXXX-1 line, then add our fields after it

      image.png.d986381d9eb816f92abb829edebcb0d4.png
      How the line is selected

      This is where I couldn't quite 'bring it home', but I did confirm you can do a preg_replace on \IPS\Settings::i()->ga_code to accomplish selecting the line that contains UA-12345678-1, then replacing it with itself plus the contents of the Stack element we created strung after it:
      image.png.87183c65773f01c011998200957f69ac.png

      All we've done here so far is break out the lines into separately manageable items that can be glued after the 'config' gtag, but the goal is to take each line, modify it so that the template logic contained therein actually works, then insert the final value into the ga_code setting. (this does not save over the contents of the setting, only changes them when it is time to display it)

      My idea for making the template logic work was something along the lines of selecting anything in {} that looks like IPS logic via regex, declaring it somewhere so that it gives us its value, then saving whatever it produces to a variable, and reinserting that variable into the line.

If I can get back to playing with this later this week, I'll certainly give it a shot.  Otherwise, any more experienced eyes would be appreciated in case I'm over-engineering it or there is some simpler solution I'm overlooking.

Ultimately what we really need is for anything called in as {setting=variable} to also have any template tags parsed when it gets displayed on the template.  I dug around looking for a way to do this, but came up empty.

 

 

 

Posted

Both member name and ID is available in JS. 

ips.getSetting( 'member_formattedName' );

ips.getSetting( 'member_id' );

Depending on where GA is added to the source you may have to wrap it in a DOM loaded event as the above variables/settings are added quite a bit down in the page source.

Posted (edited)

The code from the analytics 'integration' is added right at the start of the <head> so that will error.

Don't use the provided code box, and instead edit the global template at the right place, then the world is your oyster.

Edited by Nathan Explosion
Posted

You could, in theory, use the tag manager integration to paste the code in, as that has header and body parts.

That said though, have you considered any privacy implications of doing this sort of thing?

Posted
21 hours ago, Nathan Explosion said:

The code from the analytics 'integration' is added right at the start of the <head> so that will error.

Hence

22 hours ago, Martin A. said:

Depending on where GA is added to the source you may have to wrap it in a DOM loaded event

        <script>
            document.addEventListener("DOMContentLoaded", function()
            {
               console.log( ips.getSetting( 'member_formattedName' ) );
            });
        </script>

Doesn't really help, as member_id and member_formattedName is something added by the online/active users blocks. Unless one of those blocks is on the page, those variables are unavailable.

But there's always a way to get what you want.

<script>
            document.addEventListener("DOMContentLoaded", function()
            {
               const name = $( '#elUserLink' ).text();
               const member_id = $( '#cUserLink' ).find( 'a[href*="/profile/"]' ).attr( 'href' ).match( /profile\/(\d+?)-/ )[1];
            });
        </script>

 

Posted

I’m flying right now but just wanted to take a moment to say THANK YOU to everyone who has contributed, especially @Martin A.  

I can’t wait to try this out and report back. 

I originally had put this in the theme which does work but I’ve been intentionally trying to avoid updates to the raw HTML as it means it does not get updated automatically on upgrade. I’ve also recently added more themes so it means touching multiple themes each time as well. This would let me avoid the work all together on each update. 

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...