-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: added fn configureDefaults() to up-ga.js #2849
Conversation
f456990
to
caab985
Compare
… fix/ga-default-config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks @bjagg!
* Set the defaultConfig.config array as global settings | ||
*/ | ||
var configureDefaults = function (propertyConfig) { | ||
//console.log(propertyConfig); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the commented out logs?
Could we remove them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question and one I anticipated. I think some of the clients will need to leverage the console logs to conform their configuration is correct. I can go either way. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be okay adding some info and warning logs.
Maybe adding some around the null checks to let adopters know configuration may be missing, and info at some key points?
Also if this is an area we expect adopters to review more, we could also beef up the JSDocs a bit to give more context.
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uportal = uportal || {};
(function ($) {
/**
* Finds the appropriate property configuration for the current institution.
* @returns {Object|null} The property configuration object or null if not found.
*/
var findPropertyConfig = function () {
if (up.analytics.model == null) {
console.error("[Analytics] No analytics model found.");
return null;
}
if (Array.isArray(up.analytics.model.hosts)) {
var hosts = up.analytics.model.hosts;
var propertyConfig = null;
for (var i = 0; i < hosts.length; i++) {
var propConfig = hosts[i];
if (propConfig.name == up.analytics.host) {
propertyConfig = propConfig;
break;
}
}
if (propertyConfig != null) {
console.info("[Analytics] Found property configuration for host:", up.analytics.host);
return propertyConfig;
}
}
console.info("[Analytics] Using default property configuration.");
return up.analytics.model.defaultConfig;
};
/**
* Sets global settings from the property configuration.
* @param {Object} propertyConfig - The property configuration object.
*/
var configureDefaults = function (propertyConfig) {
var defaults = propertyConfig.config || [];
defaults.forEach(function (setting) {
Object.keys(setting).forEach(function (key) {
up.gtag('set', key, setting[key]);
});
});
};
/**
* Retrieves dimensions that apply to the current user.
* @param {Object} propertyConfig - The property configuration object.
* @returns {Object} An object containing dimension key-value pairs.
*/
var getDimensions = function (propertyConfig) {
var dimensions = {};
var dimensionGroups = propertyConfig.dimensionGroups || [];
dimensionGroups.forEach(function (setting) {
dimensions['dimension' + setting.name] = setting.value;
});
console.info("[Analytics] Dimensions set:", dimensions);
return dimensions;
};
/**
* Creates the Google Analytics tracker with the specified property ID and configuration.
* @param {Object} propertyConfig - The property configuration object.
*/
var createTracker = function (propertyConfig) {
var createSettings = {};
var configSettings = propertyConfig.config || [];
configSettings.forEach(function (setting) {
if (setting.name != 'name') {
createSettings[setting.name] = setting.value;
}
});
up.gtag('config', propertyConfig.propertyId, {
send_page_view: false,
});
console.info("[Analytics] Tracker created with property ID:", propertyConfig.propertyId);
};
/**
* Builds the page URI for a tab.
* @param {string} [fragmentName] - The fragment name.
* @param {string} [tabName] - The tab name.
* @returns {string} The constructed tab URI.
*/
var getTabUri = function (fragmentName, tabName) {
if (up.analytics.pageData.tab != null) {
fragmentName =
fragmentName || up.analytics.pageData.tab.fragmentName;
tabName = tabName || up.analytics.pageData.tab.tabName;
}
var uri = '/';
if (fragmentName != null) {
uri += 'tab/' + fragmentName;
if (tabName != null) {
uri += '/' + tabName;
}
}
return uri;
};
/**
* Retrieves variables specific to the current page.
* @param {string} [fragmentName] - The fragment name.
* @param {string} [tabName] - The tab name.
* @returns {Object} An object containing page variables.
*/
var getPageVariables = function (fragmentName, tabName) {
if (up.analytics.pageData.tab != null) {
fragmentName =
fragmentName || up.analytics.pageData.tab.fragmentName;
tabName = tabName || up.analytics.pageData.tab.tabName;
}
var title;
if (tabName != null) {
title = 'Tab: ' + tabName;
} else if (up.analytics.pageData.urlState == null) {
title = 'Portal Home';
} else {
title = 'No Tab';
}
return {
page_location: getTabUri(fragmentName, tabName),
page_title: title,
};
};
/**
* Safely resolves the portlet's fname from the windowId.
* Falls back to using the windowId if no fname is found.
* @param {string} windowId - The window ID of the portlet.
* @returns {string} The fname of the portlet.
*/
var getPortletFname = function (windowId) {
var portletData = up.analytics.portletData[windowId];
if (portletData == null) {
return windowId;
}
return portletData.fname;
};
/**
* Safely resolves the portlet's title from the windowId.
* Falls back to using getPortletFname(windowId) if the title can't be found.
* @param {string} windowId - The window ID of the portlet.
* @returns {string} The title of the portlet.
*/
var getRenderedPortletTitle = function (windowId) {
var portletWindowWrapper = $(
'div.up-portlet-windowId-content-wrapper.' + windowId
);
if (portletWindowWrapper.length == 0) {
return getPortletFname(windowId);
}
var portletWrapper = portletWindowWrapper.parents(
'div.up-portlet-wrapper-inner'
);
if (portletWrapper.length == 0) {
return getPortletFname(windowId);
}
var portletTitle = portletWrapper.find('div.up-portlet-titlebar h2 a');
if (portletTitle.length == 0) {
return getPortletFname(windowId);
}
return portletTitle.text().trim();
};
/**
* Builds the portlet URI for the specified portlet.
* @param {string} fname - The fname of the portlet.
* @returns {string} The constructed portlet URI.
*/
var getPortletUri = function (fname) {
return '/portlet/' + fname;
};
/**
* Retrieves variables specific to the specified portlet.
* @param {string} windowId - The window ID of the portlet.
* @param {Object} [portletData] - The data object of the portlet.
* @returns {Object} An object containing portlet variables.
*/
var getPortletVariables = function (windowId, portletData) {
var portletTitle = getRenderedPortletTitle(windowId);
if (portletData == null) {
portletData = up.analytics.portletData[windowId];
}
return {
page_title: 'Portlet: ' + portletTitle,
page_location: getPortletUri(portletData.fname),
};
};
/**
* Retrieves the first class name from an element that is not in the excludedClasses array.
* @param {Function} selectorFunction - A function that returns a jQuery element.
* @param {string|string[]} excludedClasses - Class name or array of class names to exclude.
* @returns {string|null} The first class name not in excludedClasses, or null if none found.
*/
var getInfoClass = function (selectorFunction, excludedClasses) {
// Ensure excludedClasses is an array
if (!Array.isArray(excludedClasses)) {
excludedClasses = [excludedClasses];
}
var classAttribute = selectorFunction().attr('class');
if (classAttribute == null) {
return null;
}
var classes = classAttribute.split(/\s+/);
for (var i = 0; i < classes.length; i++) {
var cls = classes[i];
if (excludedClasses.indexOf(cls) === -1) {
return cls;
}
}
return null;
};
/**
* Determines the fname of the portlet the clicked flyout was rendered for.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @returns {string|null} The fname of the portlet, or null if not found.
*/
var getFlyoutFname = function (clickedLink) {
return getInfoClass(function () {
return clickedLink.parents('div.up-portlet-fname-subnav-wrapper');
}, 'up-portlet-fname-subnav-wrapper');
};
/**
* Determines the windowId of the portlet the clicked external link was rendered for.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @returns {string|null} The windowId of the portlet, or null if not found.
*/
var getExternalLinkWindowId = function (clickedLink) {
return getInfoClass(function () {
return clickedLink.parents(
'div.up-portlet-windowId-content-wrapper'
);
}, 'up-portlet-windowId-content-wrapper');
};
/**
* Handles link click events for analytics tracking.
* Sends an analytics event and manages navigation behavior.
* @param {Object} event - The jQuery event object.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @param {Object} eventOptions - Additional options for the analytics event.
*/
var handleLinkClickEvent = function (event, clickedLink, eventOptions) {
// Determine if the click will open in a new window
var newWindow =
event.button == 1 ||
event.metaKey ||
event.ctrlKey ||
clickedLink.attr('target') != null;
var clickFunction;
clickFunction = newWindow
? function () {}
: function () {
document.location = clickedLink.attr('href');
};
up.gtag(
'event',
'page_view',
$.extend(
{
event_callback: clickFunction,
},
eventOptions
)
);
// If not opening a new window, prevent default behavior and set a fallback
if (!newWindow) {
// Fallback in case event_callback is not called promptly
setTimeout(clickFunction, 200);
event.preventDefault();
}
};
/**
* Adds click handlers to flyout menus to fire analytics events when used.
*/
var addFlyoutHandlers = function () {
$('ul.fl-tabs li.portal-navigation a.portal-subnav-link').click(
function (event) {
var clickedLink = $(this);
// Get the target portlet's title
var portletFlyoutTitle = clickedLink
.find('span.portal-subnav-label')
.text();
// Get the target portlet's fname
var fname = getFlyoutFname(clickedLink);
// Setup page-level variables
var pageVariables = getPageVariables();
// Send the analytics event and handle the click
handleLinkClickEvent(
event,
clickedLink,
$.extend(
{
event_category: 'Flyout Link',
event_action: getPortletUri(fname),
event_label: portletFlyoutTitle,
},
pageVariables
)
);
}
);
};
/**
* Adds handlers to inspect clicks on links and track outbound link events.
*/
var addExternalLinkHandlers = function () {
$('a').click(function (event) {
var clickedLink = $(this);
var linkHost = clickedLink.prop('hostname');
if (linkHost != '' && linkHost != document.domain) {
var windowId = getExternalLinkWindowId(clickedLink);
var eventVariables = null;
eventVariables =
windowId == null
? getPageVariables()
: getPortletVariables(windowId);
// Send the analytics event and handle the click
handleLinkClickEvent(
event,
clickedLink,
$.extend(
{
event_category: 'Outbound Link',
event_action: clickedLink.prop('href'),
event_label: clickedLink.text(),
},
eventVariables
)
);
}
});
};
/**
* Adds handlers to track "tab" clicks in the mobile accordion view.
*/
var addMobileListTabHandlers = function () {
$('ul.up-portal-nav li.up-tab').click(function () {
var clickedTab = $(this);
// Ignore clicks on already open tabs
if (clickedTab.hasClass('up-tab-open')) {
return;
}
var fragmentName = getInfoClass(function () {
return clickedTab.find('div.up-tab-owner');
}, 'up-tab-owner');
var tabName = clickedTab.find('span.up-tab-name').text().trim();
var pageVariables = getPageVariables(fragmentName, tabName);
up.gtag('event', 'page_view', pageVariables);
});
};
$(document).ready(function () {
// Initialize property configuration
var propertyConfig = findPropertyConfig();
// No property config means analytics cannot proceed
if (propertyConfig == null) {
console.error("[Analytics] No property configuration found. Analytics will not be initialized.");
return;
}
// Set default configuration
configureDefaults(propertyConfig);
// Create the tracker
createTracker(propertyConfig);
// Set dimensions for the current user
var dimensions = getDimensions(propertyConfig);
up.gtag('event', 'page_view', dimensions);
// Prepare page-level variables
var pageVariables = getPageVariables();
// Send page view event unless in MAX WindowState
if (up.analytics.pageData.urlState != 'MAX') {
up.gtag('event', 'page_view', $.extend(pageVariables, dimensions));
}
// Send timing event for page load
up.gtag(
'event',
'timing_complete',
$.extend(
{
event_category: 'tab',
name: getTabUri(),
value: up.analytics.pageData.executionTimeNano,
},
pageVariables,
dimensions
)
);
// Send events for each portlet
for (var windowId in up.analytics.portletData) {
if (up.analytics.portletData.hasOwnProperty(windowId)) {
var portletData = up.analytics.portletData[windowId];
// Skip excluded portlets
if (portletData.fname == 'google-analytics-config') {
console.info("[Analytics] Skipping portlet:", portletData.fname);
continue;
}
var portletVariables = getPortletVariables(windowId, portletData);
up.gtag('event', 'page_view', portletVariables);
up.gtag(
'event',
'timing_complete',
$.extend(
{
event_category: 'tab',
name: getTabUri(),
value: up.analytics.pageData.executionTimeNano,
},
portletVariables,
dimensions
)
);
}
}
// Add event handlers
addFlyoutHandlers();
addExternalLinkHandlers();
addMobileListTabHandlers();
});
})(jQuery);
uPortal-webapp/src/main/webapp/media/skins/common/javascript/uportal/up-ga.js
Show resolved
Hide resolved
* Set the defaultConfig.config array as global settings | ||
*/ | ||
var configureDefaults = function (propertyConfig) { | ||
//console.log(propertyConfig); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be okay adding some info and warning logs.
Maybe adding some around the null checks to let adopters know configuration may be missing, and info at some key points?
Also if this is an area we expect adopters to review more, we could also beef up the JSDocs a bit to give more context.
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
var uportal = uportal || {};
(function ($) {
/**
* Finds the appropriate property configuration for the current institution.
* @returns {Object|null} The property configuration object or null if not found.
*/
var findPropertyConfig = function () {
if (up.analytics.model == null) {
console.error("[Analytics] No analytics model found.");
return null;
}
if (Array.isArray(up.analytics.model.hosts)) {
var hosts = up.analytics.model.hosts;
var propertyConfig = null;
for (var i = 0; i < hosts.length; i++) {
var propConfig = hosts[i];
if (propConfig.name == up.analytics.host) {
propertyConfig = propConfig;
break;
}
}
if (propertyConfig != null) {
console.info("[Analytics] Found property configuration for host:", up.analytics.host);
return propertyConfig;
}
}
console.info("[Analytics] Using default property configuration.");
return up.analytics.model.defaultConfig;
};
/**
* Sets global settings from the property configuration.
* @param {Object} propertyConfig - The property configuration object.
*/
var configureDefaults = function (propertyConfig) {
var defaults = propertyConfig.config || [];
defaults.forEach(function (setting) {
Object.keys(setting).forEach(function (key) {
up.gtag('set', key, setting[key]);
});
});
};
/**
* Retrieves dimensions that apply to the current user.
* @param {Object} propertyConfig - The property configuration object.
* @returns {Object} An object containing dimension key-value pairs.
*/
var getDimensions = function (propertyConfig) {
var dimensions = {};
var dimensionGroups = propertyConfig.dimensionGroups || [];
dimensionGroups.forEach(function (setting) {
dimensions['dimension' + setting.name] = setting.value;
});
console.info("[Analytics] Dimensions set:", dimensions);
return dimensions;
};
/**
* Creates the Google Analytics tracker with the specified property ID and configuration.
* @param {Object} propertyConfig - The property configuration object.
*/
var createTracker = function (propertyConfig) {
var createSettings = {};
var configSettings = propertyConfig.config || [];
configSettings.forEach(function (setting) {
if (setting.name != 'name') {
createSettings[setting.name] = setting.value;
}
});
up.gtag('config', propertyConfig.propertyId, {
send_page_view: false,
});
console.info("[Analytics] Tracker created with property ID:", propertyConfig.propertyId);
};
/**
* Builds the page URI for a tab.
* @param {string} [fragmentName] - The fragment name.
* @param {string} [tabName] - The tab name.
* @returns {string} The constructed tab URI.
*/
var getTabUri = function (fragmentName, tabName) {
if (up.analytics.pageData.tab != null) {
fragmentName =
fragmentName || up.analytics.pageData.tab.fragmentName;
tabName = tabName || up.analytics.pageData.tab.tabName;
}
var uri = '/';
if (fragmentName != null) {
uri += 'tab/' + fragmentName;
if (tabName != null) {
uri += '/' + tabName;
}
}
return uri;
};
/**
* Retrieves variables specific to the current page.
* @param {string} [fragmentName] - The fragment name.
* @param {string} [tabName] - The tab name.
* @returns {Object} An object containing page variables.
*/
var getPageVariables = function (fragmentName, tabName) {
if (up.analytics.pageData.tab != null) {
fragmentName =
fragmentName || up.analytics.pageData.tab.fragmentName;
tabName = tabName || up.analytics.pageData.tab.tabName;
}
var title;
if (tabName != null) {
title = 'Tab: ' + tabName;
} else if (up.analytics.pageData.urlState == null) {
title = 'Portal Home';
} else {
title = 'No Tab';
}
return {
page_location: getTabUri(fragmentName, tabName),
page_title: title,
};
};
/**
* Safely resolves the portlet's fname from the windowId.
* Falls back to using the windowId if no fname is found.
* @param {string} windowId - The window ID of the portlet.
* @returns {string} The fname of the portlet.
*/
var getPortletFname = function (windowId) {
var portletData = up.analytics.portletData[windowId];
if (portletData == null) {
return windowId;
}
return portletData.fname;
};
/**
* Safely resolves the portlet's title from the windowId.
* Falls back to using getPortletFname(windowId) if the title can't be found.
* @param {string} windowId - The window ID of the portlet.
* @returns {string} The title of the portlet.
*/
var getRenderedPortletTitle = function (windowId) {
var portletWindowWrapper = $(
'div.up-portlet-windowId-content-wrapper.' + windowId
);
if (portletWindowWrapper.length == 0) {
return getPortletFname(windowId);
}
var portletWrapper = portletWindowWrapper.parents(
'div.up-portlet-wrapper-inner'
);
if (portletWrapper.length == 0) {
return getPortletFname(windowId);
}
var portletTitle = portletWrapper.find('div.up-portlet-titlebar h2 a');
if (portletTitle.length == 0) {
return getPortletFname(windowId);
}
return portletTitle.text().trim();
};
/**
* Builds the portlet URI for the specified portlet.
* @param {string} fname - The fname of the portlet.
* @returns {string} The constructed portlet URI.
*/
var getPortletUri = function (fname) {
return '/portlet/' + fname;
};
/**
* Retrieves variables specific to the specified portlet.
* @param {string} windowId - The window ID of the portlet.
* @param {Object} [portletData] - The data object of the portlet.
* @returns {Object} An object containing portlet variables.
*/
var getPortletVariables = function (windowId, portletData) {
var portletTitle = getRenderedPortletTitle(windowId);
if (portletData == null) {
portletData = up.analytics.portletData[windowId];
}
return {
page_title: 'Portlet: ' + portletTitle,
page_location: getPortletUri(portletData.fname),
};
};
/**
* Retrieves the first class name from an element that is not in the excludedClasses array.
* @param {Function} selectorFunction - A function that returns a jQuery element.
* @param {string|string[]} excludedClasses - Class name or array of class names to exclude.
* @returns {string|null} The first class name not in excludedClasses, or null if none found.
*/
var getInfoClass = function (selectorFunction, excludedClasses) {
// Ensure excludedClasses is an array
if (!Array.isArray(excludedClasses)) {
excludedClasses = [excludedClasses];
}
var classAttribute = selectorFunction().attr('class');
if (classAttribute == null) {
return null;
}
var classes = classAttribute.split(/\s+/);
for (var i = 0; i < classes.length; i++) {
var cls = classes[i];
if (excludedClasses.indexOf(cls) === -1) {
return cls;
}
}
return null;
};
/**
* Determines the fname of the portlet the clicked flyout was rendered for.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @returns {string|null} The fname of the portlet, or null if not found.
*/
var getFlyoutFname = function (clickedLink) {
return getInfoClass(function () {
return clickedLink.parents('div.up-portlet-fname-subnav-wrapper');
}, 'up-portlet-fname-subnav-wrapper');
};
/**
* Determines the windowId of the portlet the clicked external link was rendered for.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @returns {string|null} The windowId of the portlet, or null if not found.
*/
var getExternalLinkWindowId = function (clickedLink) {
return getInfoClass(function () {
return clickedLink.parents(
'div.up-portlet-windowId-content-wrapper'
);
}, 'up-portlet-windowId-content-wrapper');
};
/**
* Handles link click events for analytics tracking.
* Sends an analytics event and manages navigation behavior.
* @param {Object} event - The jQuery event object.
* @param {Object} clickedLink - The jQuery object representing the clicked link.
* @param {Object} eventOptions - Additional options for the analytics event.
*/
var handleLinkClickEvent = function (event, clickedLink, eventOptions) {
// Determine if the click will open in a new window
var newWindow =
event.button == 1 ||
event.metaKey ||
event.ctrlKey ||
clickedLink.attr('target') != null;
var clickFunction;
clickFunction = newWindow
? function () {}
: function () {
document.location = clickedLink.attr('href');
};
up.gtag(
'event',
'page_view',
$.extend(
{
event_callback: clickFunction,
},
eventOptions
)
);
// If not opening a new window, prevent default behavior and set a fallback
if (!newWindow) {
// Fallback in case event_callback is not called promptly
setTimeout(clickFunction, 200);
event.preventDefault();
}
};
/**
* Adds click handlers to flyout menus to fire analytics events when used.
*/
var addFlyoutHandlers = function () {
$('ul.fl-tabs li.portal-navigation a.portal-subnav-link').click(
function (event) {
var clickedLink = $(this);
// Get the target portlet's title
var portletFlyoutTitle = clickedLink
.find('span.portal-subnav-label')
.text();
// Get the target portlet's fname
var fname = getFlyoutFname(clickedLink);
// Setup page-level variables
var pageVariables = getPageVariables();
// Send the analytics event and handle the click
handleLinkClickEvent(
event,
clickedLink,
$.extend(
{
event_category: 'Flyout Link',
event_action: getPortletUri(fname),
event_label: portletFlyoutTitle,
},
pageVariables
)
);
}
);
};
/**
* Adds handlers to inspect clicks on links and track outbound link events.
*/
var addExternalLinkHandlers = function () {
$('a').click(function (event) {
var clickedLink = $(this);
var linkHost = clickedLink.prop('hostname');
if (linkHost != '' && linkHost != document.domain) {
var windowId = getExternalLinkWindowId(clickedLink);
var eventVariables = null;
eventVariables =
windowId == null
? getPageVariables()
: getPortletVariables(windowId);
// Send the analytics event and handle the click
handleLinkClickEvent(
event,
clickedLink,
$.extend(
{
event_category: 'Outbound Link',
event_action: clickedLink.prop('href'),
event_label: clickedLink.text(),
},
eventVariables
)
);
}
});
};
/**
* Adds handlers to track "tab" clicks in the mobile accordion view.
*/
var addMobileListTabHandlers = function () {
$('ul.up-portal-nav li.up-tab').click(function () {
var clickedTab = $(this);
// Ignore clicks on already open tabs
if (clickedTab.hasClass('up-tab-open')) {
return;
}
var fragmentName = getInfoClass(function () {
return clickedTab.find('div.up-tab-owner');
}, 'up-tab-owner');
var tabName = clickedTab.find('span.up-tab-name').text().trim();
var pageVariables = getPageVariables(fragmentName, tabName);
up.gtag('event', 'page_view', pageVariables);
});
};
$(document).ready(function () {
// Initialize property configuration
var propertyConfig = findPropertyConfig();
// No property config means analytics cannot proceed
if (propertyConfig == null) {
console.error("[Analytics] No property configuration found. Analytics will not be initialized.");
return;
}
// Set default configuration
configureDefaults(propertyConfig);
// Create the tracker
createTracker(propertyConfig);
// Set dimensions for the current user
var dimensions = getDimensions(propertyConfig);
up.gtag('event', 'page_view', dimensions);
// Prepare page-level variables
var pageVariables = getPageVariables();
// Send page view event unless in MAX WindowState
if (up.analytics.pageData.urlState != 'MAX') {
up.gtag('event', 'page_view', $.extend(pageVariables, dimensions));
}
// Send timing event for page load
up.gtag(
'event',
'timing_complete',
$.extend(
{
event_category: 'tab',
name: getTabUri(),
value: up.analytics.pageData.executionTimeNano,
},
pageVariables,
dimensions
)
);
// Send events for each portlet
for (var windowId in up.analytics.portletData) {
if (up.analytics.portletData.hasOwnProperty(windowId)) {
var portletData = up.analytics.portletData[windowId];
// Skip excluded portlets
if (portletData.fname == 'google-analytics-config') {
console.info("[Analytics] Skipping portlet:", portletData.fname);
continue;
}
var portletVariables = getPortletVariables(windowId, portletData);
up.gtag('event', 'page_view', portletVariables);
up.gtag(
'event',
'timing_complete',
$.extend(
{
event_category: 'tab',
name: getTabUri(),
value: up.analytics.pageData.executionTimeNano,
},
portletVariables,
dimensions
)
);
}
}
// Add event handlers
addFlyoutHandlers();
addExternalLinkHandlers();
addMobileListTabHandlers();
});
})(jQuery);
uPortal-webapp/src/main/webapp/media/skins/common/javascript/uportal/up-ga.js
Show resolved
Hide resolved
@bjagg I went ahead and did a quick functional test. I focused on cookie_domain which is what started all of this, and it worked as expected this time with the updated JS file. |
Hi folks, just my 2 cents from european side. Our Protection Data Officers doesn't want to see any Google analytics scripts loaded like initialized even if the configuration is empty. It would be great if this part would be inside an optional stuff, like a jsp portlet code that we won't load by removing the portlet def. I have the same problem with using some CDN links like for google fonts. Google has trackers on. |
Related, I've had pretty good experiences with Matomo https://matomo.org/ |
Matomo is largely deployed in France ;) |
This PR is focused on a configuration omission. Thanks for the code, @ChristianMurphy. I'll open an issue about making GA optional and having other solutions. You guys are awesome! |
Yes, looks like the linter and the compressor conflict. Going to revert to the previous version and capture this suggestion as a future issue once we upgrade the JS tooling. |
b2d2454
to
3aa5325
Compare
Checklist
Description of change
Default GA configuration is not being set. This change adds setting the defaults as global values.
Use of archaic JS compressor prevents use of modern ES6. Will be addressed in uPortal v6, but for now the code was rewritten in an older style that matches the rest of the file.
https://developers.google.com/analytics/devguides/collection/ga4/reference/config#:~:text=Set%20to%20'auto'%20(the,example.com%20for%20the%20domain.