Develop a Collector Plugin
Introduction
Goal
Develop a collector plugin to override collector data using the Experience manager's Alter Ego feature.
Background
The Alter Ego functionality allows a CMS user to impersonate a visitor with certain characteristics. The 'As viewed by' menu in the Experience manager always contains the option 'Alter Ego'. When the 'Alter Ego' option is selected, targeting data will be collected while previewing the channel, and targeted content will be shown. The 'Edit Alter Ego' button opens a window in which collected targeting data can be overridden with a specific value. For example, it is possible to select a specific location instead of location collected by the Relevance Module.
To be able to override collector data, a collector plugin must be provided that can edit the (JSON representation) of the targeting data. Such a plugin is similar to a characteristic plugin and provides the UI components shown in the 'Edit Alter Ego' window in the Channel Editor.
This page explains how to implement a collector plugin.
Configuration
Collector plugins are configured in the repository at:
/hippo:configuration/hippo:frontend/cms/hippo-targeting
Each collector plugin is configured in one child node of type frontend:pluginconfig. As a best practice, name the node collector-<ID of your collector>. Each collector plugin node can have the following properties:
-
collector (String, mandatory) The ID of the collector.
-
plugin.class (String, mandatory) The Java class name of the collector plugin
A collector plugin can define more configuration properties to customize the plugin.
Example: GroupsCollectorPlugin
The groups collector plugin allows you to alter the groups a user is a member of. The targeting data of the GroupsCollector simply returns the groups as a comma-separated string. The groups collector plugin consists of a checkbox group in which one or more groups can be selected.
The plugin consist of three files:
- a Java class,
- a .properties file,
- and a Javascript class.
The code shown below is a slightly simplified version of the GroupsCollectorPlugin in the Relevance Module.
Java Class
The Java class contains an @ExtClass annotation that specifies the associated Javascript class of the plugin.
GroupsCollectorPlugin.java:
package com.onehippo.cms7.targeting.frontend.plugin.groups; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.Query; import com.onehippo.cms7.targeting.frontend.plugin.CollectorPlugin; import org.hippoecm.frontend.plugin.IPluginContext; import org.hippoecm.frontend.plugin.config.IPluginConfig; import org.hippoecm.frontend.session.UserSession; import org.json.JSONException; import org.json.JSONObject; import org.wicketstuff.js.ext.util.ExtClass; /** * Plugin for the groups collector. Available plugin properties: * <ul> * <li>groups: multi-value String property, each string specifies * a selectable group</li> * </ul> */ @ExtClass("Hippo.Targeting.GroupsCollectorPlugin") @SuppressWarnings("unused") public class GroupsCollectorPlugin extends CollectorPlugin { private List<Pattern> excludes; public GroupsCollectorPlugin(final IPluginContext context, final IPluginConfig config) { super(context, config); final String[] excludesConfig = config.getStringArray("excludes"); excludes = new ArrayList<Pattern>(); if (excludesConfig != null) { for (String exclude : excludesConfig) { excludes.add(Pattern.compile(exclude)); } } } @Override protected void onRenderProperties(final JSONObject properties) throws JSONException { super.onRenderProperties(properties); try { properties.put("groups", listGroups()); } catch (RepositoryException e) { throw new JSONException(e); } } private List<String> listGroups() throws RepositoryException { final Session session = UserSession.get().getJcrSession(); final StringBuilder statement = new StringBuilder(); statement.append("//element"); statement.append("(*, ").append("hipposys:group").append(")"); statement.append(" order by @jcr:name"); final Query q = session.getWorkspace().getQueryManager() .createQuery(statement.toString(), Query.XPATH); final List<String> groups = new ArrayList<String>(); final NodeIterator nodes = q.execute().getNodes(); while (nodes.hasNext()) { final String group = nodes.nextNode().getName(); if (!isExcluded(group)) { groups.add(group); } } return groups; } private boolean isExcluded(final String group) { if (group.equals("everybody")) { return true; } for (Pattern exclude : excludes) { if (exclude.matcher(group).matches()) { return true; } } return false; }
Properties File
The .properties file contains all i18n labels. The special key collector-description is shown as the description of the collector in the 'Edit Alter Ego' window.
GroupsCollectorPlugin.properties:
collector-description=is in the user group groups-empty=No groups available no-groups=<none>
All properties are automatically available in the Javascript class as via the resources variable. For example, renderGroups method shows <none> when the list of groups is empty.
Javascript Class
(function() { "use strict"; Ext.namespace('Hippo.Targeting'); Hippo.Targeting.GroupsCollectorPlugin = Ext.extend(Hippo.Targeting.CollectorPlugin, { constructor: function(config) { var editor; if (Ext.isEmpty(config.groups)) { editor = { message: config.resources['groups-empty'], xtype: 'Hippo.Targeting.TargetingDataMessage' }; } else { editor = { collector: config.collector, groups: config.groups, resources: config.resources, xtype: 'Hippo.Targeting.GroupsTargetingDataEditor' }; } Hippo.Targeting.GroupsCollectorPlugin.superclass.constructor .call(this, Ext.apply(config, { editor: editor, renderer: this.renderGroups })); }, renderGroups: function(value) { var groups = value ? value.groups: []; if (Ext.isEmpty(groups)) { return this.resources['no-groups']; } return groups.join(', '); } }); Hippo.Targeting.GroupsTargetingDataEditor = Ext.extend(Hippo.Targeting.TargetingDataCheckboxGroup, { constructor: function(config) { var checkboxes = []; Ext.each(config.groups, function(group) { checkboxes.push({ boxLabel: group, name: group }); }); Hippo.Targeting.GroupsTargetingDataEditor.superclass .constructor.call(this, Ext.apply(config, { columns: 2, items: checkboxes, vertical: true })); }, convertDataToCheckedArray: function(data) { var checkedArray = this.createBooleanArray(this.checkboxNames .length); if (!Ext.isEmpty(data.groups)) { Ext.each(data.groups, function(dataItem) { var index = this.checkboxNames.indexOf(dataItem); if (index >= 0) { checkedArray[index] = true; } }, this); } return checkedArray; }, convertCheckedBoxesToData: function(checkedBoxes) { var checkedIds = Ext.pluck(checkedBoxes, 'name'); return { collectorId: this.collector, groups: checkedIds }; } }); Ext.reg('Hippo.Targeting.GroupsTargetingDataEditor', Hippo.Targeting.GroupsTargetingDataEditor); }());
The Javascript constructor specifies an editor and a renderer. The editor is the component used for editing the targeting data. In this case the editor is a checkbox group, but any Ext.form.Field is possible. The default editor is a textfield. The renderer is a function that converts the data string returned by the collector to a value shown in the 'Edit Alter Ego' window. The groups renderer function simply returns the string as-is, except when it is empty.
Java API
com.onehippo.cms7.targeting.frontend.plugin.CollectorPlugin
Base class for collector plugins.
Plugin configuration properties:
-
collector( String, mandatory) The ID of the collector
-
plugin.class( String, mandatory) The Java class name of the characteristic plugin
com.onehippo.cms7.targeting.frontend.plugin.dayofweek.DayOfWeekCollectorPlugin
Plugin to alter the current day of the week.
com.onehippo.cms7.targeting.frontend.plugin.geo.GeoIPCollectorPlugin
Plugin to alter the location of the visitor.
Plugin configuration properties:
-
locations(multiple String) A list of location strings to show as selectable options in the editor. Each location string has the format "city | country | latitude | longitude".
com.onehippo.cms7.targeting.frontend.plugin.groups.GroupsCollectorPlugin
Plugin to alter the groups a visitor is a member of.
Plugin configuration properties:
-
excludes(multiple String) A list of regular expression of patterns of group names to exclude from showing as selectable options in the editor.
com.onehippo.cms7.targeting.frontend.plugin.referrer.ReferrerCollectorPlugin
Plugin to alter the referrer URL.
com.onehippo.cms7.targeting.frontend.plugin.returningvisitor.ReturningVisitorCollectorPlugin
Plugin to alter whether the visitor is new or returning.
Javascript API
Hippo.Targeting.CollectorPlugin
Base class for collector plugins. A collector plugin can define its own renderer and/or editor for targeting data.
Extends: Ext.util.Observable
Properties:
-
renderer (Mixed) Optional interceptor method that transforms the targeting data string to rendered data. See Ext.grid.Column.renderer for details.
-
editor ( Ext.form.Field) Optional form field for editing the targeting data string.
Hippo.Targeting.TargetingDataCheckboxGroup
Checkbox group for editing targeting data. The default implementation iterates over a configurable property in the targeting data and assumes each element is the name of a checkbox in the group. The names of all checked checkboxes are again converted to an array and set in the targeting data. Subclasses can provide their own implementation of the methods convertDataToCheckedArray and convertCheckedBoxesToData to customize this behavior.
Extends: Ext.form.CheckboxGroup
Properties:
-
targetingDataProperty ( String) The property in the targeting data object to iterate over. Must be serialized as a JSON array.
Methods:
-
convertDataToCheckedArrayStringtargetingData\) : Array Converts the targeting data to an array of booleans that indicates which checkboxes should be checked. The default implementation iterates over a configurable property of the targeting data and assumes each element is the name of a checkbox in the group.
Parameters:
targetingData(Object): the targeting data object serialized to JSON
Returns:
An array of booleans. The Nth boolean indicates whether the Nth checkbox should be checked or not.
-
( ArraycheckedBoxes) : Array
Converts an array of Ext.form.Checkbox objects to a targeting data string. The default implementation adds the the name of each checked box to an array and sets that array in the configured targeting data property.
Parameters:
checkedBoxes (Array): an array of Ext.form.Checkbox objects that are currently checked.
Returns:
A targeting data object
Hippo.Targeting.TargetingDataMessage
'Editor' for targeting data that only displays a string. Useful for only displaying a 'no options available' message instead of the normal editor.
Extends: Ext.form.DisplayField
Properties:
-
message ( String)
The message to show.
Hippo.Targeting.TargetingDataRadioGroup
Radio group for editing targeting data. The default implementation assumes that targeting data string is the inputValue of the radio button in the group to select. Subclasses can provide their own implementation of the methods convertDataToInputValue and getValue to customize this behavior. Note that each radio button should have the same 'name' property to make them mutually exclusive. Also, commas in the radio button input values lead to incorrect behavior, so avoid those.
Extends: Ext.form.RadioGroup
Methods:
-
convertDataToInputValue( String data) : String
Converts the targeting data string to the inputValue of the radio button that should be selected. The default implementation returns the data string as-is.
Parameters:
data (String): the data string as returned by the targeting data of the collector
Returns:
The input value of the radio button to select.
-
getValue(): String
Returns the targeting data string that reflects the selected radio button. The default implementation returns the inputValue of the selected radio button, or an empty string if no radio button is selected.