In this tutorial you create a custom trait. A trait is an attribute or property of a visitor or visit, such as age or gender, that you can use to personalize content. Here we create a trait that detects the visitor's operating system. We then serve the visitor a personalized software download page: EXE download for Windows and DMG download for Mac OS X. The trait extracts the operating system from the User-Agent header. (warning) Magnolia 5.3+


Create a module

A trait is deployed with a Magnolia module. Create a module first. Choose from these options depending on your skill level:

Option 1: Clone the project in Git

Choose this option if you know how to work with a Magnolia project and Git. Choose this option also if you want to examine the code locally in your IDE. You get a ready-made project that you can customize to your needs.

  1. Clone the documentation-trait-tutorial repository.

    git clone http://git.magnolia-cms.com/git/documentation/documentation-trait-tutorial.git
  2. Import the project into your IDE. Here's what the project looks like in IntelliJ IDEA:

  3. Build the project into a JAR and deploy it to Magnolia instance. Or run the project in your IDE. To run it in an IDE, add the module as a dependency to your Magnolia bundle POM file, see option 2.

Option 2: Add the project as a dependency to your bundle

Choose this option if you run Magnolia in your IDE but don't plan to work on the code. Add the project as a dependency in the POM files of your Magnolia bundle.

For the Enterprise Edition bundle:

  1. In the dependencyManagement section of the EE-bundle parent POM file, add a dependency element with a version number:

    ee-bundle/pom.xml
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>info.magnolia.documentation</groupId>
                <artifactId>documentation-trait-tutorial</artifactId>
                <version>1.1</version>
           </dependency>
        </dependencies>
    </dependencyManagement>
    
  2. In the dependencies section of the enterprise-webapp POM file, add a dependency element without a version number:

    ee-bundle/magnolia-enterprise-webapp/pom.xml
    <dependencies>
        <dependency>
            <groupId>info.magnolia.documentation</groupId>
            <artifactId>documentation-trait-tutorial</artifactId>
        </dependency>
    </dependencies>

    (info) If you use IMB WebSphere or Oracle WebLogic, add the dependency element in the appropriate POM file of the webapp of your choice.

If you are using a custom bundle, add the dependency element with a version number in the POM file of your customized bundle.

Option 3: Download the module JAR

Choose this option if you are new to Magnolia and don't have a development environment (IDE). You can use the trait but won't be able to look at the code.

  1. Install the Magnolia Tomcat bundle.
  2. Download documentation-trait-tutorial-1.1.jar from Nexus.
  3. Copy the JAR into <CATALINA_HOME>/webapps/<contextPath>/WEB-INF/lib folder. Typically this is <CATALINA_HOME>/webapps/magnoliaAuthor/WEB-INF/lib.
  4. Restart your Magnolia instance and run the Web update. This will install the documentation-personalization-samples module.

Registering a trait

Any module can register a trait. Magnolia observes the traits folder under module configuration and adds new traits to a trait registry. Registered traits are then displayed to editors in the user interface. 

Node nameValue

 modules

 

 documentation-trait-tutorial

 

 traits

 

 userAgent

 

 ruleField

 

 valueField

 

 converterClass

info.magnolia.documentation.examples.personalization.useragent.UserAgentParameterConverter

 defaultPreviewTrait

true

 defaultRuleTrait

true

 traitClass

info.magnolia.documentation.examples.personalization.useragent.UserAgent

 voterClass

info.magnolia.documentation.examples.personalization.useragent.UserAgentVoter

Properties:

  • <trait name>: Choose a name that is easy for editors to understand such as country, date or userAgent.
    • ruleField: A field that is used to define permitted values for a trait.
    • valueField: A field that is used to enter a single value to test personalized content delivery.
    • converterClass: A converter class which must implement info.magnolia.personalization.preview.parameter.PreviewParameterConverter<Object>.
    • defaultPreviewTrait: Optional. Adds the trait to the Preview app by default. Set this property to true for any traits that editors preview content with routinely. It makes the Preview app faster to use. If a trait is used rarely, leave the property out. Default value is false. Example: defaultPreviewTrait-sc.png.
    • defaultRuleTrait: Optional. Adds the trait in the Choose audience dialog in the Pages app by default. Set this property to true for any traits that editors use routinely. It makes choosing an audience faster. If a trait is used rarely, leave the property out. Default value is false. Example: defaultRuleTrait-sc.png
    • traitClass: The class of your trait. Doesn't have to explicitly implement any interface. Usually it is just a plain Java object which allows you to specify the trait and its characteristics.

    • voterClass: The voter class must extend AbstractBoolVoter<TraitCollector>. See Voters.

Rule field

A rule field is used to define permitted values for a trait. For example, you need two rule fields to define a date range: one for start date and another for end date. Magnolia uses the input entered into the rule field(s) to construct a voter. The voter then decides whether the visitor or visit matches the rule or not. When the rule is met, personalized content is delivered.

Choose a field definition class that lets the user specify the permitted values. In some cases, a simple text field is enough (country). Other times, you must let the user specify a list of permitted values (new visitor, registered visitor, logged-in visitor). If the value is complex, use a complex field. See an example in /modules/personalization-samples/traits/visitor/ruleField.

Node nameValue

 ruleField

 

 class

info.magnolia.ui.form.field.definition.TextFieldDefinition

 type

String

Properties:

  • ruleField
    • class: The definition class of the field. The field must be known to the system. If you introduce a new field and want it only available in your module, you should configure it under the  /modules/<module name>/fieldTypes node. See Custom fields.
    • type: The type in this case is a bean property of the superclass ( TextFieldDefinition ) of the definition class. The type is not needed in every case.

The rule field is displayed to users in the Choose audience dialog.

Value field

A value field is used to enter to test variant delivery. The field is displayed to users in Preview app. When you enter a value that matches the rule, such as date that falls within the permitted date range, the personalized content variant is served. The voter tests the value against the rule.

Node nameValue

 valueField

 

 class

info.magnolia.ui.form.field.definition.TextFieldDefinition

 type

String

Properties:

  • valueField
    • class: The definition-class of the field. When introducing a new field, the same is true as mentioned above, but typically such a field is very "common".
    • type: Again the type here is a  bean-property of of the definition class.

The value field is displayed to users in the Preview app.

Filter

Once you have registered a new trait and applied it to some page variants, provide a TraitDetectorFilter and register it in the filter chain to make sure the appropriate page variant is served.

Node nameValue

 server

 

 filters

 

 context

 

 contentType

 

  userAgent

 

 class

info.magnolia.documentation.examples.personalization.useragent.UserAgentDetectorFilter

 <more filters>

 

Properties:

  • server
    • filters
      • <filter name>: Name the filter after your trait, ideally the same name as you used in trait registration. In our example the filter name is userAgent. The system doesn't require that they both have the same name but it makes it easier to identify the purpose of the filter.
        • class: A class that implements AbstractTraitDetectorFilter<Object>. In our example it should implement AbstractTraitDetectorFilter<UserAgent>.

Position of the filter in the chain

Make sure the filter is in the right position in the chain. Filters are executed in the order they are in the chain, from top to bottom. You want to position the filter right after the contentType filter.

When installing a module, you can define the position with ModuleVersionHandler#getExtraInstallTasks.

info.magnolia.documentation.examples.personalization.setup.PersonalizationExamplesModuleVersionHandler
public class PersonalizationExamplesModuleVersionHandler extends DefaultModuleVersionHandler {
    @Override
    protected List<Task> getExtraInstallTasks(InstallContext installContext) {
        List<Task> tasks = new ArrayList<Task>();
        tasks.add(new FilterOrderingTask("userAgent", new String[]{"contentType"}));
        return tasks;
    }
}

In the example above the configuration tree is simplified. Other filters may also want to be right after contentType. In a production environment it may look like this: 

Creating the pages

Now you can create page variants and choose an audience for them using the new trait. The module installs a page tree with an example page and two variants:

  • /documentation-personalization-examples-home/services/downloads/variants/variant-osx
  • /documentation-personalization-examples-home/services/downloads/variants/variant-window

Both variants have an additional "local rule" in their audience-settings:

   

Testing the result

If you did everything correctly, we can see now variations of the Downloads page (/documentation-personalization-examples-home/services/downloads) depending on the user agent. You can test this with 2 computers -  the first running Windows, the second with OS-X-version. If you do not have access to two machines with two different operating systems, use a browser plugin such as Override User Agent to impersonate a user agent.

Downloads-page on OS X:

Downloads page on Windows (actually also on a Mac using the Override User Agent plugin):

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))