Invision Community 4: SEO, prepare for v5 and dormant account notifications By Matt Monday at 02:04 PM
Randy Calvert Posted March 27, 2022 Posted March 27, 2022 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? SeNioR- 1
Marc Posted March 28, 2022 Posted March 28, 2022 I have moved this to our developer connection forum to see if anyone can assist here
IPCommerceFan Posted March 29, 2022 Posted March 29, 2022 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 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' )); Optionally display this field on the GA Form by hooking into \IPS\Helpers\Form::__construct() 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 itHow 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: 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.
Martin A. Posted March 29, 2022 Posted March 29, 2022 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.
Nathan Explosion Posted March 29, 2022 Posted March 29, 2022 (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 March 29, 2022 by Nathan Explosion
Dll Posted March 29, 2022 Posted March 29, 2022 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?
Martin A. Posted March 30, 2022 Posted March 30, 2022 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>
Randy Calvert Posted March 30, 2022 Author Posted March 30, 2022 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.
Recommended Posts