Combining DLM audience conditions for Mobile customizations

I found a couple of entries on the uPortal Mailing list that suggested to me that several people have similar requirements to myself so this is why I am making this available here.

I am using the list based mobile view and would like to have different list folders on the mobile view than I have tabs on the desktop view.  Therefore I think I need to be able to combine DLM Evaluators.

For example I want my desktop guest users to see something different to my mobile guest users.  So my mobile guest DLM fragment might look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<fragment-definition xmlns:dlm="http://org.jasig.portal.layout.dlm.config" script="classpath://org/jasig/portal/io/import-fragment-definition_v3-1.crn">
    <dlm:fragment name="Mobile Guests" ownerID="mobile-guest-lo" precedence="80">
        <dlm:audience evaluatorFactory="edu.bristol.MustMatchAllAudiencesEvaluatorFactory">
            <audience evaluatorFactory="org.jasig.portal.layout.dlm.providers.GuestUserEvaluatorFactory"/>
            <audience evaluatorFactory="org.jasig.portal.layout.dlm.providers.ProfileEvaluatorFactory">
                <paren mode="AND">
                    <profile value="mobileDefault"/>
                </paren>
            </audience>
        </dlm:audience>
    </dlm:fragment>
</fragment-definition>

 

A non-mobile guest user fragment would look almost identical to the above XML except that the mode would be "NOT" rather than "AND" for the profile value.

I have therefore created a DLM Evaluator that can combine DLM audience conditions (See attached classes - apologies for the clumsy name).  This code works by re-using the FragmentDefinition methods but defines a user as belonging to an audience only if all of the nested audience evaluations are met.

I had to tweak FragmentDefinition.java to make constructor and loadAudienceEvaluators() method protected rather than private.  So that my EvaluatorFactory would simply consist of:

public class MustMatchAllAudiencesEvaluatorFactory extends FragmentDefinition implements EvaluatorFactory {

    public Evaluator getEvaluator(Node node) {
        Element e = (Element) node;
        loadAudienceEvaluators(e.getElementsByTagName("audience"));
        return new MustMatchAllAudiencesEvaluator(evaluators);
    }

}


after which point the Evaluator's core method just needs to conduct the evaluations in order:

    public MustMatchAllAudiencesEvaluator(List<Evaluator> evaluators) {
        this.evaluators = evaluators;
    }

    @Override
    public boolean isApplicable(IPerson ip) {
        int matches = 0;
        for (Evaluator e : evaluators) {
            if (e.isApplicable(ip)) {
                matches++;
            } else {
                break;
            }
        }
        return matches == evaluators.size();
    }



I then added my Evaluator into ehcache.xml and hibernate.cfg.xml in order to make the new Evaluator cacheable and persistent.  Seems to work.