Oracle WebCenter Sites: Analytics Implementation Guide | OpsBlu Docs

Oracle WebCenter Sites: Analytics Implementation Guide

Technical guide to implementing analytics on Oracle WebCenter Sites, covering Template/CSElement injection, Satellite Server caching, and asset-model...

Analytics Architecture on Oracle WebCenter Sites

Oracle WebCenter Sites (WCS) uses an asset-based content model where every piece of content -- pages, articles, images -- is stored as a typed asset in the database. Pages are rendered through Templates and CSElements, which are server-side rendering constructs that assemble HTML from asset data.

The request flow:

Request → Web Server → Satellite Server (cache) → WebCenter Sites App Server → Template Rendering → HTML

Satellite Server is the caching/delivery layer. It caches rendered page fragments and full pages, serving them without hitting the application server. This is similar to a reverse proxy cache but integrated directly into the WCS architecture. It uses cache keys based on asset IDs, dimensions, and parameters.

Oracle CX (Customer Experience) suite integration provides the personalization and analytics layer on top of WCS. If you use Oracle CX alongside third-party analytics, both share the same rendered page.


Installing Tracking Scripts

Method 1: Template-Level Injection

WCS Templates are the top-level rendering construct for page assets. Add analytics scripts in the page Template:

<%-- PageTemplate.jsp (WebCenter Sites Template) --%>
<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld" %>
<%@ taglib prefix="asset" uri="futuretense_cs/asset.tld" %>
<%@ taglib prefix="render" uri="futuretense_cs/render.tld" %>

<cs:ftcs>
<html>
<head>
  <render:calltemplate tname="HTMLHead" args="c,cid" />

  <%-- Analytics Data Layer --%>
  <render:callelement elementname="Analytics/DataLayer" args="c,cid" />

  <%-- GTM Container --%>
  <script async src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX"></script>
</head>
<body>
  <render:calltemplate tname="PageBody" args="c,cid" />

  <noscript>
    <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXX"
            height="0" width="0" style="display:none;visibility:hidden"></iframe>
  </noscript>
</body>
</html>
</cs:ftcs>

Method 2: CSElement for Reusable Script Blocks

Create a CSElement (callable element) that encapsulates the analytics script. This allows reuse across multiple Templates:

<%-- Elements/Analytics/TrackingScript.jsp --%>
<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld" %>

<cs:ftcs>
<%
  // Read tracking ID from site configuration asset
  String trackingId = "";
  ICS ics = (ICS) pageContext.getAttribute("ics", PageContext.REQUEST_SCOPE);
  String siteName = ics.GetVar("site");

  // Look up site config asset
  FTValList args = new FTValList();
  args.setValString("ASSET_TYPE", "SiteConfig");
  args.setValString("FIELD", "trackingId");
  args.setValString("SITENAME", siteName);
  trackingId = ics.SelectTo("SiteConfig", args, "trackingId");
%>

<% if (trackingId != null && !trackingId.isEmpty()) { %>
  <script>
    window.dataLayer = window.dataLayer || [];
  </script>
  <script async src="https://www.googletagmanager.com/gtm.js?id=<%= trackingId %>"></script>
<% } %>
</cs:ftcs>

Call it from any Template:

<render:callelement elementname="Analytics/TrackingScript" />

Method 3: SiteEntry / Controller Integration

For more complex scenarios, inject analytics configuration at the SiteEntry (controller) level before template rendering:

// Custom SiteEntry or Wrapper element
public class AnalyticsSiteEntry implements ICSElement {
    public void execute(ICS ics) throws CSRuntimeException {
        String env = ics.GetProperty("cs.environment", "dev");
        if ("prod".equals(env)) {
            ics.SetVar("analyticsEnabled", "true");
            ics.SetVar("gtmId", "GTM-XXXXX");
        } else {
            ics.SetVar("analyticsEnabled", "false");
        }
    }
}

Data Layer Implementation

Build the data layer from asset metadata in a CSElement:

<%-- Elements/Analytics/DataLayer.jsp --%>
<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld" %>
<%@ taglib prefix="asset" uri="futuretense_cs/asset.tld" %>

<cs:ftcs>
<%
  ICS ics = (ICS) pageContext.getAttribute("ics", PageContext.REQUEST_SCOPE);
  String assetType = ics.GetVar("c");
  String assetId = ics.GetVar("cid");

  // Load the current asset
  AssetData assetData = AssetDataManager.read(ics, new AssetIdImpl(assetType, Long.parseLong(assetId)));

  String title = assetData.getAttributeData("title") != null
    ? assetData.getAttributeData("title").getData().toString() : "";
  String path = assetData.getAttributeData("path") != null
    ? assetData.getAttributeData("path").getData().toString() : "";
  String template = assetData.getAttributeData("template") != null
    ? assetData.getAttributeData("template").getData().toString() : "";
  String siteName = ics.GetVar("site");
  String locale = ics.GetVar("locale");
%>

<script>
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    platform: 'oracle_webcenter_sites',
    assetType: '<%= assetType %>',
    assetId: '<%= assetId %>',
    pageTitle: '<%= title.replace("'", "\\'") %>',
    pagePath: '<%= path.replace("'", "\\'") %>',
    template: '<%= template %>',
    site: '<%= siteName %>',
    locale: '<%= locale %>'
  });
</script>
</cs:ftcs>

For multi-valued attributes (taxonomy, tags), iterate and serialize:

<%
  List<String> tags = new ArrayList<>();
  AttributeData tagData = assetData.getAttributeData("tags");
  if (tagData != null && tagData.getDataAsList() != null) {
    for (Object tag : tagData.getDataAsList()) {
      tags.add(tag.toString());
    }
  }
%>
<script>
  window.dataLayer[window.dataLayer.length - 1].tags = <%= new Gson().toJson(tags) %>;
</script>

Common Issues

Satellite Server Caching Data Layer Values

Satellite Server caches rendered HTML fragments, including inline <script> blocks. If the data layer contains user-specific or session-specific values, they get cached with the first render.

Solution 1: Exclude the data layer CSElement from Satellite Server caching using the UNCACHED directive:

<render:callelement elementname="Analytics/DataLayer"
                    args="c,cid"
                    cachecontrol="false" />

Or mark the element as uncacheable in the element definition:

<!-- In the CSElement asset properties -->
<property name="cachecontrol" value="false"/>

Solution 2: Separate static and dynamic data. Cache the static page data layer (content metadata) and fetch dynamic data (user state, session info) via client-side AJAX to an uncached endpoint.

Dimension-Based Caching Creating Multiple Data Layer Versions

WCS supports dimension-based caching (locale, device type, segment). Each dimension combination gets its own cached copy. Verify that your data layer correctly reflects the active dimensions:

<%
  String dimension = ics.GetVar("d");
  String deviceType = ics.GetVar("DeviceGroup");
%>
<script>
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    dimension: '<%= dimension %>',
    deviceType: '<%= deviceType %>'
  });
</script>

Preview/Contribution Mode Sending Analytics

WCS has multiple modes: delivery (live), contribution (editing), and preview. Analytics should only fire in delivery mode:

<%
  String mode = ics.GetVar("rendermode");
  boolean isDelivery = "live".equals(mode) || mode == null;
%>
<% if (isDelivery) { %>
  <script async src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX"></script>
<% } %>

Asset Flex Family Queries Failing in Data Layer

If your data layer pulls data from flex assets using AssetDataManager, ensure the flex family and attribute definitions exist on the delivery environment. Missing attribute definitions return null silently, which produces empty data layer fields.


Platform-Specific Considerations

Asset model architecture -- Everything in WCS is an asset with a type and ID. Pages, articles, images, and configuration are all assets. Your data layer should expose the asset type and ID as primary identifiers, since these are the canonical references across the WCS ecosystem.

Template vs. CSElement -- Templates are tied to asset types and render full pages. CSElements are reusable fragments that can be called from any Template. Place the GTM container in a CSElement for reuse; place the data layer in a Template-called CSElement that receives the current asset context via c (asset type) and cid (asset ID) arguments.

Oracle CX integration -- Oracle CX (Infinity, Maxymiser, Responsys) can be integrated alongside third-party analytics. Oracle Infinity uses its own tag (ora.click) and data collection endpoint. If running both Oracle CX and GTM, ensure they do not conflict by giving each its own data layer variable or by feeding Oracle CX through GTM as a tag.

Multi-site with Satellite Server -- WCS supports multiple sites in a single installation. Each site has its own Satellite Server cache namespace. When implementing analytics across sites, use site-specific CSElements or parameterize a shared CSElement with the site name to ensure the correct tracking ID is used per site. The ics.GetVar("site") call returns the current site name.

Migration to Oracle Content Management (OCM) -- Oracle is transitioning customers from WebCenter Sites to Oracle Content Management (cloud-native). If you are mid-migration, you may need analytics implementations on both platforms simultaneously. OCM uses a REST API and headless delivery, so analytics shifts to the frontend application layer.