Adding Piwik Web Analytics to Liferay
Table of Contents [-]
Liferay, as we all know, is a horse of a different color. Using piwik in Liferay is difficult because:
- Liferay pages are not static.
- Liferay pages are generated from Velocity.
- Information necessary for calling piwik is not really available at necessary points in Liferay.
To begin with, I certainly didn't need or want to be able to leverage all possible piwik functionality. My only goal was to be able to track hits on my navigatable pages of my Liferay-based site.
So if you implement everything I've outlined below, you'll be able to track page views of your portal using just a custom jar, some additions to portal-ext.properties, a new servlet filter for the ROOT web application (Liferay), and a small addition to your portal-normal.vm template.
Before diving into the changes, a brief note about the architecture I went with.
First of all, I knew that information from the original incoming HttpServletRequest needed to be captured and stored so it would be available when it came time to call the Piwik server. Using Liferay auditing as a model, I created a ThreadLocal class to hold a POJO called PiwikVisitorInfo. This POJO has the necessary member fields to hold all of the information piwik would need, from a visitor perspective, to track page views. To populate and maintain the POJO, I created a filter that is inserted into the normal Liferay filter chain. Liferay 6.1 will allow using a hook to deploy a servlet filter, but as I'm still on 6.0 the filter must be added manually.
To handle the minor configuration changes, I've defined some new properties that must be set in portal-ext.properties for the Piwik site id (used to tell Piwik that the page views were on one site or another) and the Piwik server (so it wouldn't need to be hard-coded anywhere).
To make pieces of the code available to the velocity templates, I needed to inject some Spring beans into Liferay's Spring context. To do this I've included an XML file with the Spring bean definitions; another change to portal-ext.properties is necessary to ensure it gets loaded at server startup.
There is some code involved. This code is bundled into a jar and must be put into the ROOT/WEB-INF/lib folder. You can either manually copy this jar in or you can use an EXT plugin to deploy it.
Quick Start #
Basically you're going to do the following:
- Download the piwik-liferay.jar file and get it in the ROOT/WEB-INF/lib directory.
- Edit ROOT/WEB-INF/web.xml (for CE) or ROOT/WEB-INF/liferay-web.xml (for EE) to add the filter.
- Edit ROOT/WEB-INF/classes/portal-ext.properties to add the new properties, enable the filter, and pull in the piwik.spring.xml file during Liferay startup.
- Restart your app server.
- Edit the portal-normal.vm file of your custom theme to add a few lines and deploy it.
Download the piwik-liferay.jar file #
I'm attaching the precompiled jar file to this wiki page. I'll also include the Eclipse project (zipped w/ source) if you want to check it out.
Download the piwik-liferay.jar file and put it into the ROOT/WEB-INF/lib directory. It may be possible to put it in the global lib directory (i.e. Tomcat's lib/ext directory), although I haven't tried it. If you want to deploy the jar using the normal Liferay way, you'll need to create an EXT plugin and put the jar into the EXT project's docroot/WEB-INF/ext-lib/portal directory and deploy the EXT plugin to Liferay.
Adding the Servlet Filter #
The piwik servlet filter will be invoked for many incoming requests. Adding the filter is a manual process outlined below.
If you're running CE, you're going to edit ROOT/WEB-INF/web.xml. If you're running EE, you're going to edit ROOT/WEB-INF/liferay-web.xml. The changes are the same regardless of which file you're editing.
You're going to be adding two XML stanzas to the appropriate XML file.
The first stanza below defines the filter:
<filter> <filter-name>Piwik Filter</filter-name> <filter-class>org.piwik.liferay.servlet.filter.PiwikServletFilter</filter-class> <init-param> <param-name>url-regex-ignore-pattern</param-name> <param-value>^/html/.+\.(css|gif|html|ico|jpg|js|png)(\?.*)?$</param-value> </init-param> </filter>
The next stanza defines the filter mapping:
<filter-mapping> <filter-name>Piwik Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
When inserting these two stanzas into the XML file, be sure to put them in the appropriate locations in the file. For those that don't know, add the <filter /> stanza where you see other <filter /> tags, and the <filter-mapping /> stanza around the other <filter-mapping /> tags in the file.
Portal Properties #
There are some portal-ext.properties file additions that you'll need to make:
# Enable the Piwik servlet filter org.piwik.liferay.servlet.filter.PiwikServletFilter=true # Define the URL for your piwik server # piwik.url=http://piwik.example.com/piwik/ piwik.url=http://localhost/piwik/ # Define the site id this Liferay instance will report page hits to. # Sites should already be defined in the piwik admin interface. piwik.site.id=2 # Add the piwik-spring.xml to the list of xml files to load when Liferay starts spring.configs=\ [see note],\ META-INF/piwik-spring.xml
Note: For the spring.configs guy, you're actually overriding the default value in portal.properties. You're going to want to copy the current value from portal.properties and use for this property, you're just going to add the META-INF/piwik-spring.xml to the end.
Adding Tracking to portal-normal.vm #
After restarting your application server, you'll now be ready to make changes to your theme's portal-normal.vm file to add page view tracking.
Open the portal-normal.vm file in the _diffs/templates directory of your custom theme. Just above the closing </body> tag at the bottom of the page, add the following:
#set( $piwikTracker = $utilLocator.findUtil("org.piwik.liferay.PiwikTracker") ) #if( $piwikTracker) #set( $piwikResult = $piwikTracker.trackPageView($the_title) ) #end
Deploy your theme and start browsing pages. On your piwik server, you should now see some hits coming from your Liferay box.
Visitor Custom Variables #
Piwik supports using visitor custom variables, or variables that are defined at the visitor level. The Liferay piwik implementation defines and populates four custom variables:
- realUserId The user id logged into the system.
- realUserName The user's real name.
- userId The effective user id (when impersonating a user, this is the id of the impersonated user).
- userName The effective user name (when impersonating a user, this is the name of the impersonated user).
Other Features Implemented But Not Tested #
The piwik-liferay jar has other features implemented, but they have not been tested:
- Page-level details, including support for page custom variables.
- Other types of tracking:
- Link tracking
- Download tracking
- Goal tracking
- E-Commerce tracking
The cookies include data such as the visitor id, the browser window size, and some of the browser plugins that may be installed (such as quicktime, java, flash, etc.).
Since the code I've presented is not running in the browser, these cookies are not defined, they're not sent, and they're not tracked in piwik.
You could, if needed, manually include the piwik.js file, but this may have side effects that interfere with the overall portal page.
Also, after piwik is called and it has third party cookies enabled, a tracking cookie will be returned by piwik. Again, since this implementation is not running in the browser, the tracking cookie does not get sent back to the browser and the information is lost.
#set( $piwikTracker = $utilLocator.findUtil("org.piwik.liferay.PiwikTracker") ) #if( $piwikTracker) #set( $piwikResult = $piwikTracker.trackPageView($the_title) ) #if( $piwikResult) $piwikResult #end #end
Note that the tracking cookie name is defined in the global piwik configuration (global.ini.php) and the default value does not match the cookie name the code is looking for (a string that contains "id.#.", where the # is the site id), so you'll probably need to make modifications to the code to match the global.ini.php configuration before using it.
Page Info #
Piwik actually uses two different types of information: visitor information and page information. The visitor information is populated by the PiwikServletFilter and ensures that the visitor information is correct.
For page information, the PiwikTracker interface includes a method, getPageInfo(). The getPageInfo() method returns an instance of PiwikPageInfo that you can use to supply additional information such as goal campaigns, page custom variables, and forced timestamps.
To use this in Velocity, you would have code like:
#set( $piwikTracker = $utilLocator.findUtil("org.piwik.liferay.PiwikTracker") ) #if( $piwikTracker) #set( $piwikPage = $piwikTracker.getPageInfo() ) #if( $piwikPage) #set( $piwikPage = $piwikPage.addCustomVariable("myPageVar","value") ) #set( $piwikPage = $piwikPage.setGoalCampaignName("My Campaign") ) #end #set( $piwikResult = $piwikTracker.trackPageView($the_title, $piwikPage) ) #end
The PiwikPageInfo object returns a reference to itself for all setters so it can be safely used in velocity.
The PiwikPageInfo object, when populated, is added as the last argument for all of the tracking methods.
Piwik Release Inconsistencies #
Finally, it is important to note that it seems like Piwik.org does not keep any of their code releases in sync. The piwik server code seems to be developed and maintained separately from the PiwikTracker.php code (for tracking within a PHP-based site) and is certainly out of sync with the Java tracker code available on the site.
So while what I've provided will work as-is for basic page tracking, some of the code will require some tweaking on your end to be able to use 100% of the features. You'll probably have to access not only the java code, but the piwik source (including the piwik.js file and possibly core/Tracker.php and core/Tracker/Visit.php) and PiwikTracker.php to identify what changes need to be made.
First, this implementation would not have been possible without some information from the piwik.org site, specifically some java code I downloaded from Piwik Ticket 2172 and Piwik Ticket 3151. The code had to be significantly refactored (the posted code was intended to be added to a JSP page), but it was a great starting point.
Also crucial was the Piwik Tracking API page for documenting the tracking API (including changes made in newer versions of the code that have not yet been replicated into the java tracker).
I've just uploaded the piwik-liferay-1.1 stuff. You should be able to use this in place of the 1.0 (unlabeled) versions.
1.0 - 07/20/2012 #
Initial release and Wiki page created.
1.1 - 07/21/2012 #
1.1 includes the following:
- Change for the browser info cookie name.
- Adds a PiwikTrackerUtil class (similar to the Liferay Service Builder's XxxLocalServiceUtil classes) so external plugins can access the PiwikTracker instance the portal is using.
- Add in the code for the E-Commerce support in the latest version of PiwikTracker.php script.
- Tweaks as to how the piwik visitor id is handled.